// // Copyright 2015 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. // // DisplayWGL.h: WGL implementation of egl::Display #include "libANGLE/renderer/gl/wgl/DisplayWGL.h" #include "common/debug.h" #include "libANGLE/Config.h" #include "libANGLE/Context.h" #include "libANGLE/Display.h" #include "libANGLE/Surface.h" #include "libANGLE/renderer/gl/ContextGL.h" #include "libANGLE/renderer/gl/RendererGL.h" #include "libANGLE/renderer/gl/renderergl_utils.h" #include "libANGLE/renderer/gl/wgl/ContextWGL.h" #include "libANGLE/renderer/gl/wgl/D3DTextureSurfaceWGL.h" #include "libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h" #include "libANGLE/renderer/gl/wgl/FunctionsWGL.h" #include "libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.h" #include "libANGLE/renderer/gl/wgl/RendererWGL.h" #include "libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h" #include "libANGLE/renderer/gl/wgl/wgl_utils.h" #include "platform/Platform.h" #include #include #include namespace rx { namespace { std::string GetErrorMessage() { DWORD errorCode = GetLastError(); LPSTR messageBuffer = nullptr; size_t size = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); std::string message(messageBuffer, size); if (size == 0) { std::ostringstream stream; stream << "Failed to get the error message for '" << errorCode << "' due to the error '" << GetLastError() << "'"; message = stream.str(); } if (messageBuffer != nullptr) { LocalFree(messageBuffer); } return message; } } // anonymous namespace class FunctionsGLWindows : public FunctionsGL { public: FunctionsGLWindows(HMODULE openGLModule, PFNWGLGETPROCADDRESSPROC getProcAddressWGL) : mOpenGLModule(openGLModule), mGetProcAddressWGL(getProcAddressWGL) { ASSERT(mOpenGLModule); ASSERT(mGetProcAddressWGL); } ~FunctionsGLWindows() override {} private: void *loadProcAddress(const std::string &function) const override { void *proc = reinterpret_cast(mGetProcAddressWGL(function.c_str())); if (!proc) { proc = reinterpret_cast(GetProcAddress(mOpenGLModule, function.c_str())); } return proc; } HMODULE mOpenGLModule; PFNWGLGETPROCADDRESSPROC mGetProcAddressWGL; }; DisplayWGL::DisplayWGL(const egl::DisplayState &state) : DisplayGL(state), mRenderer(nullptr), mCurrentData(), mOpenGLModule(nullptr), mFunctionsWGL(nullptr), mHasWGLCreateContextRobustness(false), mHasRobustness(false), mWindowClass(0), mWindow(nullptr), mDeviceContext(nullptr), mPixelFormat(0), mUseDXGISwapChains(false), mHasDXInterop(false), mDxgiModule(nullptr), mD3d11Module(nullptr), mD3D11DeviceHandle(nullptr), mD3D11Device(nullptr), mUseARBShare(true) {} DisplayWGL::~DisplayWGL() {} egl::Error DisplayWGL::initialize(egl::Display *display) { egl::Error error = initializeImpl(display); if (error.isError()) { destroy(); return error; } return DisplayGL::initialize(display); } egl::Error DisplayWGL::initializeImpl(egl::Display *display) { mDisplayAttributes = display->getAttributeMap(); mOpenGLModule = LoadLibraryExA("opengl32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); if (!mOpenGLModule) { return egl::EglNotInitialized() << "Failed to load OpenGL library."; } mFunctionsWGL = new FunctionsWGL(); mFunctionsWGL->initialize(mOpenGLModule, nullptr); // WGL can't grab extensions until it creates a context because it needs to load the driver's // DLLs first. Create a dummy context to load the driver and determine which GL versions are // available. // Work around compile error from not defining "UNICODE" while Chromium does const LPSTR idcArrow = MAKEINTRESOURCEA(32512); std::ostringstream stream; stream << "ANGLE DisplayWGL " << gl::FmtHex(display) << " Intermediate Window Class"; std::string className = stream.str(); WNDCLASSA intermediateClassDesc = {}; intermediateClassDesc.style = CS_OWNDC; intermediateClassDesc.lpfnWndProc = DefWindowProcA; intermediateClassDesc.cbClsExtra = 0; intermediateClassDesc.cbWndExtra = 0; intermediateClassDesc.hInstance = GetModuleHandle(nullptr); intermediateClassDesc.hIcon = nullptr; intermediateClassDesc.hCursor = LoadCursorA(nullptr, idcArrow); intermediateClassDesc.hbrBackground = 0; intermediateClassDesc.lpszMenuName = nullptr; intermediateClassDesc.lpszClassName = className.c_str(); mWindowClass = RegisterClassA(&intermediateClassDesc); if (!mWindowClass) { return egl::EglNotInitialized() << "Failed to register intermediate OpenGL window class \"" << className.c_str() << "\":" << gl::FmtErr(HRESULT_CODE(GetLastError())); } HWND dummyWindow = CreateWindowExA(0, reinterpret_cast(mWindowClass), "ANGLE Dummy Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, nullptr, nullptr); if (!dummyWindow) { return egl::EglNotInitialized() << "Failed to create dummy OpenGL window."; } HDC dummyDeviceContext = GetDC(dummyWindow); if (!dummyDeviceContext) { return egl::EglNotInitialized() << "Failed to get the device context of the dummy OpenGL window."; } const PIXELFORMATDESCRIPTOR pixelFormatDescriptor = wgl::GetDefaultPixelFormatDescriptor(); int dummyPixelFormat = ChoosePixelFormat(dummyDeviceContext, &pixelFormatDescriptor); if (dummyPixelFormat == 0) { return egl::EglNotInitialized() << "Could not find a compatible pixel format for the dummy OpenGL window."; } if (!SetPixelFormat(dummyDeviceContext, dummyPixelFormat, &pixelFormatDescriptor)) { return egl::EglNotInitialized() << "Failed to set the pixel format on the intermediate OpenGL window."; } HGLRC dummyWGLContext = mFunctionsWGL->createContext(dummyDeviceContext); if (!dummyDeviceContext) { return egl::EglNotInitialized() << "Failed to create a WGL context for the dummy OpenGL window."; } if (!mFunctionsWGL->makeCurrent(dummyDeviceContext, dummyWGLContext)) { return egl::EglNotInitialized() << "Failed to make the dummy WGL context current."; } // Reinitialize the wgl functions to grab the extensions mFunctionsWGL->initialize(mOpenGLModule, dummyDeviceContext); mHasWGLCreateContextRobustness = mFunctionsWGL->hasExtension("WGL_ARB_create_context_robustness"); // Destroy the dummy window and context mFunctionsWGL->makeCurrent(dummyDeviceContext, nullptr); mFunctionsWGL->deleteContext(dummyWGLContext); ReleaseDC(dummyWindow, dummyDeviceContext); DestroyWindow(dummyWindow); const egl::AttributeMap &displayAttributes = display->getAttributeMap(); EGLint requestedDisplayType = static_cast(displayAttributes.get( EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE)); if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE && !mFunctionsWGL->hasExtension("WGL_EXT_create_context_es2_profile") && !mFunctionsWGL->hasExtension("WGL_EXT_create_context_es_profile")) { return egl::EglNotInitialized() << "Cannot create an OpenGL ES platform on Windows without " "the WGL_EXT_create_context_es(2)_profile extension."; } // Create the real intermediate context and windows mWindow = CreateWindowExA(0, reinterpret_cast(mWindowClass), "ANGLE Intermediate Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, nullptr, nullptr); if (!mWindow) { return egl::EglNotInitialized() << "Failed to create intermediate OpenGL window."; } mDeviceContext = GetDC(mWindow); if (!mDeviceContext) { return egl::EglNotInitialized() << "Failed to get the device context of the intermediate OpenGL window."; } if (mFunctionsWGL->choosePixelFormatARB) { std::vector attribs = wgl::GetDefaultPixelFormatAttributes(false); UINT matchingFormats = 0; mFunctionsWGL->choosePixelFormatARB(mDeviceContext, &attribs[0], nullptr, 1u, &mPixelFormat, &matchingFormats); } if (mPixelFormat == 0) { mPixelFormat = ChoosePixelFormat(mDeviceContext, &pixelFormatDescriptor); } if (mPixelFormat == 0) { return egl::EglNotInitialized() << "Could not find a compatible pixel format for the intermediate OpenGL window."; } if (!SetPixelFormat(mDeviceContext, mPixelFormat, &pixelFormatDescriptor)) { return egl::EglNotInitialized() << "Failed to set the pixel format on the intermediate OpenGL window."; } ANGLE_TRY(createRenderer(&mRenderer)); const FunctionsGL *functionsGL = mRenderer->getFunctions(); mHasRobustness = functionsGL->getGraphicsResetStatus != nullptr; if (mHasWGLCreateContextRobustness != mHasRobustness) { WARN() << "WGL_ARB_create_context_robustness exists but unable to create a context with " "robustness."; } // Intel OpenGL ES drivers are not currently supported due to bugs in the driver and ANGLE VendorID vendor = GetVendorID(functionsGL); if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE && IsIntel(vendor)) { return egl::EglNotInitialized() << "Intel OpenGL ES drivers are not supported."; } // Create DXGI swap chains for windows that come from other processes. Windows is unable to // SetPixelFormat on windows from other processes when a sandbox is enabled. HDC nativeDisplay = display->getNativeDisplayId(); HWND nativeWindow = WindowFromDC(nativeDisplay); if (nativeWindow != nullptr) { DWORD currentProcessId = GetCurrentProcessId(); DWORD windowProcessId; GetWindowThreadProcessId(nativeWindow, &windowProcessId); // AMD drivers advertise the WGL_NV_DX_interop and WGL_NV_DX_interop2 extensions but fail mUseDXGISwapChains = !IsAMD(vendor) && (currentProcessId != windowProcessId); } else { mUseDXGISwapChains = false; } mHasDXInterop = mFunctionsWGL->hasExtension("WGL_NV_DX_interop2"); if (mUseDXGISwapChains) { if (mHasDXInterop) { ANGLE_TRY(initializeD3DDevice()); } else { // Want to use DXGI swap chains but WGL_NV_DX_interop2 is not present, fail // initialization return egl::EglNotInitialized() << "WGL_NV_DX_interop2 is required but not present."; } } const gl::Version &maxVersion = mRenderer->getMaxSupportedESVersion(); if (maxVersion < gl::Version(2, 0)) { return egl::EglNotInitialized() << "OpenGL ES 2.0 is not supportable."; } return egl::NoError(); } void DisplayWGL::terminate() { DisplayGL::terminate(); destroy(); } void DisplayWGL::destroy() { releaseD3DDevice(mD3D11DeviceHandle); mRenderer.reset(); if (mFunctionsWGL) { if (mDeviceContext) { mFunctionsWGL->makeCurrent(mDeviceContext, nullptr); } } mCurrentData.clear(); SafeDelete(mFunctionsWGL); if (mDeviceContext) { ReleaseDC(mWindow, mDeviceContext); mDeviceContext = nullptr; } if (mWindow) { DestroyWindow(mWindow); mWindow = nullptr; } if (mWindowClass) { if (!UnregisterClassA(reinterpret_cast(mWindowClass), GetModuleHandle(nullptr))) { WARN() << "Failed to unregister OpenGL window class: " << gl::FmtHex(mWindowClass); } mWindowClass = NULL; } if (mOpenGLModule) { FreeLibrary(mOpenGLModule); mOpenGLModule = nullptr; } SafeRelease(mD3D11Device); if (mDxgiModule) { FreeLibrary(mDxgiModule); mDxgiModule = nullptr; } if (mD3d11Module) { FreeLibrary(mD3d11Module); mD3d11Module = nullptr; } ASSERT(mRegisteredD3DDevices.empty()); } SurfaceImpl *DisplayWGL::createWindowSurface(const egl::SurfaceState &state, EGLNativeWindowType window, const egl::AttributeMap &attribs) { EGLint orientation = static_cast(attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0)); if (mUseDXGISwapChains) { egl::Error error = initializeD3DDevice(); if (error.isError()) { return nullptr; } return new DXGISwapChainWindowSurfaceWGL( state, mRenderer->getStateManager(), window, mD3D11Device, mD3D11DeviceHandle, mDeviceContext, mRenderer->getFunctions(), mFunctionsWGL, orientation); } else { return new WindowSurfaceWGL(state, window, mPixelFormat, mFunctionsWGL, orientation); } } SurfaceImpl *DisplayWGL::createPbufferSurface(const egl::SurfaceState &state, const egl::AttributeMap &attribs) { EGLint width = static_cast(attribs.get(EGL_WIDTH, 0)); EGLint height = static_cast(attribs.get(EGL_HEIGHT, 0)); bool largest = (attribs.get(EGL_LARGEST_PBUFFER, EGL_FALSE) == EGL_TRUE); EGLenum textureFormat = static_cast(attribs.get(EGL_TEXTURE_FORMAT, EGL_NO_TEXTURE)); EGLenum textureTarget = static_cast(attribs.get(EGL_TEXTURE_TARGET, EGL_NO_TEXTURE)); return new PbufferSurfaceWGL(state, width, height, textureFormat, textureTarget, largest, mPixelFormat, mDeviceContext, mFunctionsWGL); } SurfaceImpl *DisplayWGL::createPbufferFromClientBuffer(const egl::SurfaceState &state, EGLenum buftype, EGLClientBuffer clientBuffer, const egl::AttributeMap &attribs) { egl::Error error = initializeD3DDevice(); if (error.isError()) { return nullptr; } return new D3DTextureSurfaceWGL(state, mRenderer->getStateManager(), buftype, clientBuffer, this, mDeviceContext, mD3D11Device, mRenderer->getFunctions(), mFunctionsWGL); } SurfaceImpl *DisplayWGL::createPixmapSurface(const egl::SurfaceState &state, NativePixmapType nativePixmap, const egl::AttributeMap &attribs) { UNIMPLEMENTED(); return nullptr; } rx::ContextImpl *DisplayWGL::createContext(const gl::State &state, gl::ErrorSet *errorSet, const egl::Config *configuration, const gl::Context *shareContext, const egl::AttributeMap &attribs) { return new ContextWGL(state, errorSet, mRenderer); } DeviceImpl *DisplayWGL::createDevice() { UNREACHABLE(); return nullptr; } egl::ConfigSet DisplayWGL::generateConfigs() { egl::ConfigSet configs; int minSwapInterval = 1; int maxSwapInterval = 1; if (mFunctionsWGL->swapIntervalEXT) { // No defined maximum swap interval in WGL_EXT_swap_control, use a reasonable number minSwapInterval = 0; maxSwapInterval = 8; } const gl::Version &maxVersion = getMaxSupportedESVersion(); ASSERT(maxVersion >= gl::Version(2, 0)); bool supportsES3 = maxVersion >= gl::Version(3, 0); PIXELFORMATDESCRIPTOR pixelFormatDescriptor; DescribePixelFormat(mDeviceContext, mPixelFormat, sizeof(pixelFormatDescriptor), &pixelFormatDescriptor); auto getAttrib = [this](int attrib) { return wgl::QueryWGLFormatAttrib(mDeviceContext, mPixelFormat, attrib, mFunctionsWGL); }; const EGLint optimalSurfaceOrientation = mUseDXGISwapChains ? EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE : 0; egl::Config config; config.renderTargetFormat = GL_RGBA8; // TODO: use the bit counts to determine the format config.depthStencilFormat = GL_DEPTH24_STENCIL8; // TODO: use the bit counts to determine the format config.bufferSize = pixelFormatDescriptor.cColorBits; config.redSize = pixelFormatDescriptor.cRedBits; config.greenSize = pixelFormatDescriptor.cGreenBits; config.blueSize = pixelFormatDescriptor.cBlueBits; config.luminanceSize = 0; config.alphaSize = pixelFormatDescriptor.cAlphaBits; config.alphaMaskSize = 0; config.bindToTextureRGB = (getAttrib(WGL_BIND_TO_TEXTURE_RGB_ARB) == TRUE); config.bindToTextureRGBA = (getAttrib(WGL_BIND_TO_TEXTURE_RGBA_ARB) == TRUE); config.colorBufferType = EGL_RGB_BUFFER; config.configCaveat = EGL_NONE; config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0); config.depthSize = pixelFormatDescriptor.cDepthBits; config.level = 0; config.matchNativePixmap = EGL_NONE; config.maxPBufferWidth = getAttrib(WGL_MAX_PBUFFER_WIDTH_ARB); config.maxPBufferHeight = getAttrib(WGL_MAX_PBUFFER_HEIGHT_ARB); config.maxPBufferPixels = getAttrib(WGL_MAX_PBUFFER_PIXELS_ARB); config.maxSwapInterval = maxSwapInterval; config.minSwapInterval = minSwapInterval; config.nativeRenderable = EGL_TRUE; // Direct rendering config.nativeVisualID = 0; config.nativeVisualType = EGL_NONE; config.renderableType = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0); config.sampleBuffers = 0; // FIXME: enumerate multi-sampling config.samples = 0; config.stencilSize = pixelFormatDescriptor.cStencilBits; config.surfaceType = ((pixelFormatDescriptor.dwFlags & PFD_DRAW_TO_WINDOW) ? EGL_WINDOW_BIT : 0) | ((getAttrib(WGL_DRAW_TO_PBUFFER_ARB) == TRUE) ? EGL_PBUFFER_BIT : 0) | ((getAttrib(WGL_SWAP_METHOD_ARB) == WGL_SWAP_COPY_ARB) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0); config.optimalOrientation = optimalSurfaceOrientation; config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; config.transparentType = EGL_NONE; config.transparentRedValue = 0; config.transparentGreenValue = 0; config.transparentBlueValue = 0; configs.add(config); return configs; } bool DisplayWGL::testDeviceLost() { if (mHasRobustness) { return mRenderer->getResetStatus() != gl::GraphicsResetStatus::NoError; } return false; } egl::Error DisplayWGL::restoreLostDevice(const egl::Display *display) { return egl::EglBadDisplay(); } bool DisplayWGL::isValidNativeWindow(EGLNativeWindowType window) const { return (IsWindow(window) == TRUE); } egl::Error DisplayWGL::validateClientBuffer(const egl::Config *configuration, EGLenum buftype, EGLClientBuffer clientBuffer, const egl::AttributeMap &attribs) const { switch (buftype) { case EGL_D3D_TEXTURE_ANGLE: case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE: ANGLE_TRY(const_cast(this)->initializeD3DDevice()); return D3DTextureSurfaceWGL::ValidateD3DTextureClientBuffer(buftype, clientBuffer, mD3D11Device); default: return DisplayGL::validateClientBuffer(configuration, buftype, clientBuffer, attribs); } } std::string DisplayWGL::getVendorString() const { // UNIMPLEMENTED(); return ""; } egl::Error DisplayWGL::initializeD3DDevice() { if (mD3D11Device != nullptr) { return egl::NoError(); } mDxgiModule = LoadLibrary(TEXT("dxgi.dll")); if (!mDxgiModule) { return egl::EglNotInitialized() << "Failed to load DXGI library."; } mD3d11Module = LoadLibrary(TEXT("d3d11.dll")); if (!mD3d11Module) { return egl::EglNotInitialized() << "Failed to load d3d11 library."; } PFN_D3D11_CREATE_DEVICE d3d11CreateDevice = nullptr; d3d11CreateDevice = reinterpret_cast( GetProcAddress(mD3d11Module, "D3D11CreateDevice")); if (d3d11CreateDevice == nullptr) { return egl::EglNotInitialized() << "Could not retrieve D3D11CreateDevice address."; } HRESULT result = d3d11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &mD3D11Device, nullptr, nullptr); if (FAILED(result)) { return egl::EglNotInitialized() << "Could not create D3D11 device, " << gl::FmtHR(result); } return registerD3DDevice(mD3D11Device, &mD3D11DeviceHandle); } void DisplayWGL::generateExtensions(egl::DisplayExtensions *outExtensions) const { // Only enable the surface orientation and post sub buffer for DXGI swap chain surfaces, they // prefer to swap with inverted Y. outExtensions->postSubBuffer = mUseDXGISwapChains; outExtensions->surfaceOrientation = mUseDXGISwapChains; outExtensions->createContextRobustness = mHasRobustness; outExtensions->d3dTextureClientBuffer = mHasDXInterop; outExtensions->d3dShareHandleClientBuffer = mHasDXInterop; outExtensions->surfaceD3DTexture2DShareHandle = true; outExtensions->querySurfacePointer = true; outExtensions->keyedMutex = true; // Contexts are virtualized so textures can be shared globally outExtensions->displayTextureShareGroup = true; outExtensions->surfacelessContext = true; DisplayGL::generateExtensions(outExtensions); } void DisplayWGL::generateCaps(egl::Caps *outCaps) const { outCaps->textureNPOT = true; } egl::Error DisplayWGL::makeCurrentSurfaceless(gl::Context *context) { // Nothing to do because WGL always uses the same context and the previous surface can be left // current. return egl::NoError(); } egl::Error DisplayWGL::waitClient(const gl::Context *context) { // Unimplemented as this is not needed for WGL return egl::NoError(); } egl::Error DisplayWGL::waitNative(const gl::Context *context, EGLint engine) { // Unimplemented as this is not needed for WGL return egl::NoError(); } egl::Error DisplayWGL::makeCurrent(egl::Surface *drawSurface, egl::Surface *readSurface, gl::Context *context) { CurrentNativeContext ¤tContext = mCurrentData[std::this_thread::get_id()]; HDC newDC = currentContext.dc; if (drawSurface) { SurfaceWGL *drawSurfaceWGL = GetImplAs(drawSurface); newDC = drawSurfaceWGL->getDC(); } else { newDC = mDeviceContext; } HGLRC newContext = currentContext.glrc; if (context) { ContextWGL *contextWGL = GetImplAs(context); newContext = contextWGL->getContext(); } else if (!mUseDXGISwapChains) { newContext = 0; } if (newDC != currentContext.dc || newContext != currentContext.glrc) { ASSERT(newDC != 0); if (!mFunctionsWGL->makeCurrent(newDC, newContext)) { // TODO(geofflang): What error type here? return egl::EglContextLost() << "Failed to make the WGL context current."; } currentContext.dc = newDC; currentContext.glrc = newContext; } return DisplayGL::makeCurrent(drawSurface, readSurface, context); } egl::Error DisplayWGL::registerD3DDevice(IUnknown *device, HANDLE *outHandle) { ASSERT(device != nullptr); ASSERT(outHandle != nullptr); auto iter = mRegisteredD3DDevices.find(device); if (iter != mRegisteredD3DDevices.end()) { iter->second.refCount++; *outHandle = iter->second.handle; return egl::NoError(); } HANDLE handle = mFunctionsWGL->dxOpenDeviceNV(device); if (!handle) { return egl::EglBadParameter() << "Failed to open D3D device."; } device->AddRef(); D3DObjectHandle newDeviceInfo; newDeviceInfo.handle = handle; newDeviceInfo.refCount = 1; mRegisteredD3DDevices[device] = newDeviceInfo; *outHandle = handle; return egl::NoError(); } void DisplayWGL::releaseD3DDevice(HANDLE deviceHandle) { for (auto iter = mRegisteredD3DDevices.begin(); iter != mRegisteredD3DDevices.end(); iter++) { if (iter->second.handle == deviceHandle) { iter->second.refCount--; if (iter->second.refCount == 0) { mFunctionsWGL->dxCloseDeviceNV(iter->second.handle); iter->first->Release(); mRegisteredD3DDevices.erase(iter); break; } } } } gl::Version DisplayWGL::getMaxSupportedESVersion() const { return mRenderer->getMaxSupportedESVersion(); } void DisplayWGL::destroyNativeContext(HGLRC context) { mFunctionsWGL->deleteContext(context); } HGLRC DisplayWGL::initializeContextAttribs(const egl::AttributeMap &eglAttributes, HGLRC &sharedContext, bool &useARBShare, std::vector &workerContextAttribs) const { EGLint requestedDisplayType = static_cast( eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE)); // Create a context of the requested version, if any. gl::Version requestedVersion(static_cast(eglAttributes.get( EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE)), static_cast(eglAttributes.get( EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE))); if (static_cast(requestedVersion.major) != EGL_DONT_CARE && static_cast(requestedVersion.minor) != EGL_DONT_CARE) { int profileMask = 0; if (requestedDisplayType != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE && requestedVersion >= gl::Version(3, 2)) { profileMask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; } return createContextAttribs(requestedVersion, profileMask, sharedContext, useARBShare, workerContextAttribs); } // Try all the GL version in order as a workaround for Mesa context creation where the driver // doesn't automatically return the highest version available. for (const auto &info : GenerateContextCreationToTry(requestedDisplayType, false)) { int profileFlag = 0; if (info.type == ContextCreationTry::Type::DESKTOP_CORE) { profileFlag |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; } else if (info.type == ContextCreationTry::Type::ES) { profileFlag |= WGL_CONTEXT_ES_PROFILE_BIT_EXT; } HGLRC context = createContextAttribs(info.version, profileFlag, sharedContext, useARBShare, workerContextAttribs); if (context != nullptr) { return context; } } return nullptr; } HGLRC DisplayWGL::createContextAttribs(const gl::Version &version, int profileMask, HGLRC &sharedContext, bool &useARBShare, std::vector &workerContextAttribs) const { std::vector attribs; if (mHasWGLCreateContextRobustness) { attribs.push_back(WGL_CONTEXT_FLAGS_ARB); attribs.push_back(WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB); attribs.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB); attribs.push_back(WGL_LOSE_CONTEXT_ON_RESET_ARB); } attribs.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); attribs.push_back(version.major); attribs.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); attribs.push_back(version.minor); if (profileMask != 0) { attribs.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); attribs.push_back(profileMask); } attribs.push_back(0); attribs.push_back(0); HGLRC context = mFunctionsWGL->createContextAttribsARB(mDeviceContext, nullptr, &attribs[0]); // This shared context is never made current. It is safer than the main context to be used as // a seed to create worker contexts from. // It seems a WGL restriction not mentioned in MSDN, but some posts revealed it. // https://www.opengl.org/discussion_boards/showthread.php/152648-wglShareLists-failing // https://github.com/glfw/glfw/issues/402 sharedContext = mFunctionsWGL->createContextAttribsARB(mDeviceContext, context, &attribs[0]); workerContextAttribs = attribs; useARBShare = true; return context; } egl::Error DisplayWGL::createRenderer(std::shared_ptr *outRenderer) { HGLRC context = nullptr; HGLRC sharedContext = nullptr; std::vector workerContextAttribs; if (mFunctionsWGL->createContextAttribsARB) { context = initializeContextAttribs(mDisplayAttributes, sharedContext, mUseARBShare, workerContextAttribs); } // If wglCreateContextAttribsARB is unavailable or failed, try the standard wglCreateContext if (!context) { // Don't have control over GL versions context = mFunctionsWGL->createContext(mDeviceContext); } if (!context) { return egl::EglNotInitialized() << "Failed to create a WGL context for the intermediate OpenGL window." << GetErrorMessage(); } if (!sharedContext) { sharedContext = mFunctionsWGL->createContext(mDeviceContext); if (!mFunctionsWGL->shareLists(context, sharedContext)) { mFunctionsWGL->deleteContext(sharedContext); sharedContext = nullptr; } mUseARBShare = false; } if (!mFunctionsWGL->makeCurrent(mDeviceContext, context)) { return egl::EglNotInitialized() << "Failed to make the intermediate WGL context current."; } CurrentNativeContext ¤tContext = mCurrentData[std::this_thread::get_id()]; currentContext.dc = mDeviceContext; currentContext.glrc = context; std::unique_ptr functionsGL( new FunctionsGLWindows(mOpenGLModule, mFunctionsWGL->getProcAddress)); functionsGL->initialize(mDisplayAttributes); outRenderer->reset(new RendererWGL(std::move(functionsGL), mDisplayAttributes, this, context, sharedContext, workerContextAttribs)); return egl::NoError(); } class WorkerContextWGL final : public WorkerContext { public: WorkerContextWGL(FunctionsWGL *functions, HPBUFFERARB pbuffer, HDC deviceContext, HGLRC context); ~WorkerContextWGL() override; bool makeCurrent() override; void unmakeCurrent() override; private: FunctionsWGL *mFunctionsWGL; HPBUFFERARB mPbuffer; HDC mDeviceContext; HGLRC mContext; }; WorkerContextWGL::WorkerContextWGL(FunctionsWGL *functions, HPBUFFERARB pbuffer, HDC deviceContext, HGLRC context) : mFunctionsWGL(functions), mPbuffer(pbuffer), mDeviceContext(deviceContext), mContext(context) {} WorkerContextWGL::~WorkerContextWGL() { mFunctionsWGL->makeCurrent(mDeviceContext, nullptr); mFunctionsWGL->deleteContext(mContext); mFunctionsWGL->releasePbufferDCARB(mPbuffer, mDeviceContext); mFunctionsWGL->destroyPbufferARB(mPbuffer); } bool WorkerContextWGL::makeCurrent() { bool result = mFunctionsWGL->makeCurrent(mDeviceContext, mContext); if (!result) { ERR() << GetErrorMessage(); } return result; } void WorkerContextWGL::unmakeCurrent() { mFunctionsWGL->makeCurrent(mDeviceContext, nullptr); } WorkerContext *DisplayWGL::createWorkerContext(std::string *infoLog, HGLRC sharedContext, const std::vector &workerContextAttribs) { if (!sharedContext) { *infoLog += "Unable to create the shared context."; return nullptr; } HPBUFFERARB workerPbuffer = nullptr; HDC workerDeviceContext = nullptr; HGLRC workerContext = nullptr; #define CLEANUP_ON_ERROR() \ do \ { \ if (workerContext) \ { \ mFunctionsWGL->deleteContext(workerContext); \ } \ if (workerDeviceContext) \ { \ mFunctionsWGL->releasePbufferDCARB(workerPbuffer, workerDeviceContext); \ } \ if (workerPbuffer) \ { \ mFunctionsWGL->destroyPbufferARB(workerPbuffer); \ } \ } while (0) const int attribs[] = {0, 0}; workerPbuffer = mFunctionsWGL->createPbufferARB(mDeviceContext, mPixelFormat, 1, 1, attribs); if (!workerPbuffer) { *infoLog += GetErrorMessage(); return nullptr; } workerDeviceContext = mFunctionsWGL->getPbufferDCARB(workerPbuffer); if (!workerDeviceContext) { *infoLog += GetErrorMessage(); CLEANUP_ON_ERROR(); return nullptr; } if (mUseARBShare) { workerContext = mFunctionsWGL->createContextAttribsARB(mDeviceContext, sharedContext, &workerContextAttribs[0]); } else { workerContext = mFunctionsWGL->createContext(workerDeviceContext); } if (!workerContext) { GetErrorMessage(); CLEANUP_ON_ERROR(); return nullptr; } if (!mUseARBShare && !mFunctionsWGL->shareLists(sharedContext, workerContext)) { GetErrorMessage(); CLEANUP_ON_ERROR(); return nullptr; } #undef CLEANUP_ON_ERROR return new WorkerContextWGL(mFunctionsWGL, workerPbuffer, workerDeviceContext, workerContext); } void DisplayWGL::initializeFrontendFeatures(angle::FrontendFeatures *features) const { mRenderer->initializeFrontendFeatures(features); } void DisplayWGL::populateFeatureList(angle::FeatureList *features) { mRenderer->getFeatures().populateFeatureList(features); } } // namespace rx