| // |
| // Copyright (c) 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. |
| // |
| |
| // DXGISwapChainWindowSurfaceWGL.cpp: WGL implementation of egl::Surface for windows using a DXGI |
| // swapchain. |
| |
| #include "libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h" |
| |
| #include "libANGLE/formatutils.h" |
| #include "libANGLE/renderer/gl/FramebufferGL.h" |
| #include "libANGLE/renderer/gl/TextureGL.h" |
| #include "libANGLE/renderer/gl/RendererGL.h" |
| #include "libANGLE/renderer/gl/StateManagerGL.h" |
| #include "libANGLE/renderer/gl/wgl/DisplayWGL.h" |
| #include "libANGLE/renderer/gl/wgl/FunctionsWGL.h" |
| |
| #include <EGL/eglext.h> |
| |
| namespace rx |
| { |
| |
| DXGISwapChainWindowSurfaceWGL::DXGISwapChainWindowSurfaceWGL(const egl::SurfaceState &state, |
| RendererGL *renderer, |
| EGLNativeWindowType window, |
| ID3D11Device *device, |
| HANDLE deviceHandle, |
| HGLRC wglContext, |
| HDC deviceContext, |
| const FunctionsGL *functionsGL, |
| const FunctionsWGL *functionsWGL, |
| EGLint orientation) |
| : SurfaceGL(state, renderer), |
| mWindow(window), |
| mStateManager(renderer->getStateManager()), |
| mWorkarounds(renderer->getWorkarounds()), |
| mRenderer(renderer), |
| mFunctionsGL(functionsGL), |
| mFunctionsWGL(functionsWGL), |
| mDevice(device), |
| mDeviceHandle(deviceHandle), |
| mWGLDevice(deviceContext), |
| mWGLContext(wglContext), |
| mSwapChainFormat(DXGI_FORMAT_UNKNOWN), |
| mSwapChainFlags(0), |
| mDepthBufferFormat(GL_NONE), |
| mFirstSwap(true), |
| mSwapChain(nullptr), |
| mSwapChain1(nullptr), |
| mColorRenderbufferID(0), |
| mRenderbufferBufferHandle(nullptr), |
| mDepthRenderbufferID(0), |
| mFramebufferID(0), |
| mTextureID(0), |
| mTextureHandle(nullptr), |
| mWidth(0), |
| mHeight(0), |
| mSwapInterval(1), |
| mOrientation(orientation) |
| { |
| } |
| |
| DXGISwapChainWindowSurfaceWGL::~DXGISwapChainWindowSurfaceWGL() |
| { |
| if (mRenderbufferBufferHandle != nullptr) |
| { |
| mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, 1, &mRenderbufferBufferHandle); |
| mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mRenderbufferBufferHandle); |
| } |
| |
| if (mColorRenderbufferID != 0) |
| { |
| mStateManager->deleteRenderbuffer(mColorRenderbufferID); |
| mColorRenderbufferID = 0; |
| } |
| |
| if (mDepthRenderbufferID != 0) |
| { |
| mStateManager->deleteRenderbuffer(mDepthRenderbufferID); |
| mDepthRenderbufferID = 0; |
| } |
| |
| SafeRelease(mSwapChain); |
| SafeRelease(mSwapChain1); |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::initialize(const DisplayImpl *displayImpl) |
| { |
| if (mOrientation != EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE) |
| { |
| // TODO(geofflang): Support the orientation extensions fully. Currently only inverting Y is |
| // supported. To support all orientations, an intermediate framebuffer will be needed with |
| // a blit before swap. |
| return egl::Error(EGL_BAD_ATTRIBUTE, |
| "DXGISwapChainWindowSurfaceWGL requires an orientation of " |
| "EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE."); |
| } |
| |
| RECT rect; |
| if (!GetClientRect(mWindow, &rect)) |
| { |
| return egl::Error(EGL_BAD_NATIVE_WINDOW, "Failed to query the window size."); |
| } |
| mWidth = rect.right - rect.left; |
| mHeight = rect.bottom - rect.top; |
| |
| mSwapChainFormat = DXGI_FORMAT_R8G8B8A8_UNORM; |
| mSwapChainFlags = 0; |
| mDepthBufferFormat = GL_DEPTH24_STENCIL8; |
| |
| mFunctionsGL->genFramebuffers(1, &mFramebufferID); |
| mFunctionsGL->genRenderbuffers(1, &mColorRenderbufferID); |
| mFunctionsGL->genRenderbuffers(1, &mDepthRenderbufferID); |
| |
| return createSwapChain(); |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::makeCurrent() |
| { |
| if (!mFunctionsWGL->makeCurrent(mWGLDevice, mWGLContext)) |
| { |
| // TODO: What error type here? |
| return egl::Error(EGL_CONTEXT_LOST, "Failed to make the WGL context current."); |
| } |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::swap(const DisplayImpl *displayImpl) |
| { |
| mFunctionsGL->flush(); |
| |
| egl::Error error = setObjectsLocked(false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| HRESULT result = mSwapChain->Present(mSwapInterval, 0); |
| mFirstSwap = false; |
| |
| error = setObjectsLocked(true); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| if (FAILED(result)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to present swap chain, result: 0x%X", result); |
| } |
| |
| return checkForResize(); |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::postSubBuffer(EGLint x, |
| EGLint y, |
| EGLint width, |
| EGLint height) |
| { |
| ASSERT(mSwapChain1 != nullptr); |
| |
| mFunctionsGL->flush(); |
| |
| egl::Error error = setObjectsLocked(false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| HRESULT result = S_OK; |
| if (mFirstSwap) |
| { |
| result = mSwapChain1->Present(mSwapInterval, 0); |
| mFirstSwap = false; |
| } |
| else |
| { |
| RECT rect = {static_cast<LONG>(x), static_cast<LONG>(mHeight - y - height), |
| static_cast<LONG>(x + width), static_cast<LONG>(mHeight - y)}; |
| DXGI_PRESENT_PARAMETERS params = {1, &rect, nullptr, nullptr}; |
| result = mSwapChain1->Present1(mSwapInterval, 0, ¶ms); |
| } |
| |
| error = setObjectsLocked(true); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| if (FAILED(result)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to present swap chain, result: 0x%X", result); |
| } |
| |
| return checkForResize(); |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::querySurfacePointerANGLE(EGLint attribute, void **value) |
| { |
| UNREACHABLE(); |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::bindTexImage(gl::Texture *texture, EGLint buffer) |
| { |
| ASSERT(mTextureHandle == nullptr); |
| |
| const TextureGL *textureGL = GetImplAs<TextureGL>(texture); |
| GLuint textureID = textureGL->getTextureID(); |
| |
| ID3D11Texture2D *colorBuffer = nullptr; |
| HRESULT result = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), |
| reinterpret_cast<void **>(&colorBuffer)); |
| if (FAILED(result)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to query texture from swap chain, result: 0x%X", |
| result); |
| } |
| |
| mTextureHandle = mFunctionsWGL->dxRegisterObjectNV(mDeviceHandle, colorBuffer, textureID, |
| GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV); |
| SafeRelease(colorBuffer); |
| if (mTextureHandle == nullptr) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to register D3D object, error: 0x%08x.", |
| HRESULT_CODE(GetLastError())); |
| } |
| |
| if (!mFunctionsWGL->dxLockObjectsNV(mDeviceHandle, 1, &mTextureHandle)) |
| { |
| mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mTextureHandle); |
| mTextureHandle = nullptr; |
| |
| return egl::Error(EGL_BAD_ALLOC, "Failed to lock D3D object, error: 0x%08x.", |
| HRESULT_CODE(GetLastError())); |
| } |
| |
| mTextureID = textureID; |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::releaseTexImage(EGLint buffer) |
| { |
| ASSERT(mTextureHandle != nullptr); |
| |
| if (!mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, 1, &mTextureHandle)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to unlock D3D object, error: 0x%08x.", |
| HRESULT_CODE(GetLastError())); |
| } |
| |
| if (!mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mTextureHandle)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to unregister D3D object, error: 0x%08x.", |
| HRESULT_CODE(GetLastError())); |
| } |
| |
| mTextureID = 0; |
| mTextureHandle = nullptr; |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| void DXGISwapChainWindowSurfaceWGL::setSwapInterval(EGLint interval) |
| { |
| mSwapInterval = interval; |
| } |
| |
| EGLint DXGISwapChainWindowSurfaceWGL::getWidth() const |
| { |
| return static_cast<EGLint>(mWidth); |
| } |
| |
| EGLint DXGISwapChainWindowSurfaceWGL::getHeight() const |
| { |
| return static_cast<EGLint>(mHeight); |
| } |
| |
| EGLint DXGISwapChainWindowSurfaceWGL::isPostSubBufferSupported() const |
| { |
| return mSwapChain1 != nullptr; |
| } |
| |
| EGLint DXGISwapChainWindowSurfaceWGL::getSwapBehavior() const |
| { |
| return EGL_BUFFER_DESTROYED; |
| } |
| |
| FramebufferImpl *DXGISwapChainWindowSurfaceWGL::createDefaultFramebuffer( |
| const gl::FramebufferState &data) |
| { |
| return new FramebufferGL(mFramebufferID, data, mFunctionsGL, mWorkarounds, |
| mRenderer->getBlitter(), mStateManager); |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::setObjectsLocked(bool locked) |
| { |
| if (mRenderbufferBufferHandle == nullptr) |
| { |
| ASSERT(mTextureHandle == nullptr); |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| HANDLE resources[] = { |
| mRenderbufferBufferHandle, mTextureHandle, |
| }; |
| GLint count = (mTextureHandle != nullptr) ? 2 : 1; |
| |
| if (locked) |
| { |
| if (!mFunctionsWGL->dxLockObjectsNV(mDeviceHandle, count, resources)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to lock object, error: 0x%08x.", |
| HRESULT_CODE(GetLastError())); |
| } |
| } |
| else |
| { |
| if (!mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, count, resources)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to lock object, error: 0x%08x.", |
| HRESULT_CODE(GetLastError())); |
| } |
| } |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::checkForResize() |
| { |
| RECT rect; |
| if (!GetClientRect(mWindow, &rect)) |
| { |
| return egl::Error(EGL_BAD_NATIVE_WINDOW, "Failed to query the window size."); |
| } |
| |
| size_t newWidth = rect.right - rect.left; |
| size_t newHeight = rect.bottom - rect.top; |
| if (newWidth != mWidth || newHeight != mHeight) |
| { |
| mWidth = newWidth; |
| mHeight = newHeight; |
| |
| // TODO(geofflang): Handle resize by resizing the swap chain instead of re-creating it. |
| egl::Error error = createSwapChain(); |
| if (error.isError()) |
| { |
| return error; |
| } |
| } |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| static IDXGIFactory *GetDXGIFactoryFromDevice(ID3D11Device *device) |
| { |
| IDXGIDevice *dxgiDevice = nullptr; |
| HRESULT result = |
| device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void **>(&dxgiDevice)); |
| if (FAILED(result)) |
| { |
| return nullptr; |
| } |
| |
| IDXGIAdapter *dxgiAdapter = nullptr; |
| result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void **>(&dxgiAdapter)); |
| SafeRelease(dxgiDevice); |
| if (FAILED(result)) |
| { |
| return nullptr; |
| } |
| |
| IDXGIFactory *dxgiFactory = nullptr; |
| result = |
| dxgiAdapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast<void **>(&dxgiFactory)); |
| SafeRelease(dxgiAdapter); |
| if (FAILED(result)) |
| { |
| return nullptr; |
| } |
| |
| return dxgiFactory; |
| } |
| |
| egl::Error DXGISwapChainWindowSurfaceWGL::createSwapChain() |
| { |
| egl::Error error = setObjectsLocked(false); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| if (mRenderbufferBufferHandle) |
| { |
| mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mRenderbufferBufferHandle); |
| mRenderbufferBufferHandle = nullptr; |
| } |
| |
| // If this surface is bound to a texture, unregister it. |
| bool hadBoundSurface = (mTextureHandle != nullptr); |
| if (hadBoundSurface) |
| { |
| mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mTextureHandle); |
| mTextureHandle = nullptr; |
| } |
| |
| IDXGIFactory *dxgiFactory = GetDXGIFactoryFromDevice(mDevice); |
| if (dxgiFactory == nullptr) |
| { |
| return egl::Error(EGL_BAD_NATIVE_WINDOW, "Failed to query the DXGIFactory."); |
| } |
| |
| IDXGIFactory2 *dxgiFactory2 = nullptr; |
| HRESULT result = dxgiFactory->QueryInterface(__uuidof(IDXGIFactory2), |
| reinterpret_cast<void **>(&dxgiFactory2)); |
| if (SUCCEEDED(result)) |
| { |
| ASSERT(dxgiFactory2 != nullptr); |
| |
| DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0}; |
| swapChainDesc.BufferCount = 1; |
| swapChainDesc.Format = mSwapChainFormat; |
| swapChainDesc.Width = static_cast<UINT>(mWidth); |
| swapChainDesc.Height = static_cast<UINT>(mHeight); |
| swapChainDesc.Format = mSwapChainFormat; |
| swapChainDesc.Stereo = FALSE; |
| swapChainDesc.SampleDesc.Count = 1; |
| swapChainDesc.SampleDesc.Quality = 0; |
| swapChainDesc.BufferUsage = |
| DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER; |
| swapChainDesc.BufferCount = 1; |
| swapChainDesc.Scaling = DXGI_SCALING_STRETCH; |
| swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; |
| swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; |
| swapChainDesc.Flags = mSwapChainFlags; |
| |
| result = dxgiFactory2->CreateSwapChainForHwnd(mDevice, mWindow, &swapChainDesc, nullptr, |
| nullptr, &mSwapChain1); |
| SafeRelease(dxgiFactory2); |
| SafeRelease(dxgiFactory); |
| if (FAILED(result)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to create swap chain for window, result: 0x%X", |
| result); |
| } |
| |
| mSwapChain = mSwapChain1; |
| mSwapChain->AddRef(); |
| } |
| else |
| { |
| DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; |
| swapChainDesc.BufferCount = 1; |
| swapChainDesc.BufferDesc.Format = mSwapChainFormat; |
| swapChainDesc.BufferDesc.Width = static_cast<UINT>(mWidth); |
| swapChainDesc.BufferDesc.Height = static_cast<UINT>(mHeight); |
| swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; |
| swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; |
| swapChainDesc.BufferDesc.RefreshRate.Numerator = 0; |
| swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; |
| swapChainDesc.BufferUsage = |
| DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER; |
| swapChainDesc.Flags = mSwapChainFlags; |
| swapChainDesc.OutputWindow = mWindow; |
| swapChainDesc.SampleDesc.Count = 1; |
| swapChainDesc.SampleDesc.Quality = 0; |
| swapChainDesc.Windowed = TRUE; |
| swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; |
| |
| result = dxgiFactory->CreateSwapChain(mDevice, &swapChainDesc, &mSwapChain); |
| SafeRelease(dxgiFactory); |
| if (FAILED(result)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to create swap chain for window, result: 0x%X", |
| result); |
| } |
| } |
| |
| ID3D11Texture2D *colorBuffer = nullptr; |
| result = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), |
| reinterpret_cast<void **>(&colorBuffer)); |
| if (FAILED(result)) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to query texture from swap chain, result: 0x%X", |
| result); |
| } |
| |
| mFunctionsGL->genRenderbuffers(1, &mColorRenderbufferID); |
| mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mColorRenderbufferID); |
| mRenderbufferBufferHandle = |
| mFunctionsWGL->dxRegisterObjectNV(mDeviceHandle, colorBuffer, mColorRenderbufferID, |
| GL_RENDERBUFFER, WGL_ACCESS_READ_WRITE_NV); |
| SafeRelease(colorBuffer); |
| if (mRenderbufferBufferHandle == nullptr) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to register D3D object, error: 0x%X.", |
| HRESULT_CODE(GetLastError())); |
| } |
| |
| // Rebind the surface to the texture if needed. |
| if (hadBoundSurface) |
| { |
| mTextureHandle = mFunctionsWGL->dxRegisterObjectNV(mDeviceHandle, colorBuffer, mTextureID, |
| GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV); |
| if (mTextureHandle == nullptr) |
| { |
| return egl::Error(EGL_BAD_ALLOC, "Failed to register D3D object, error: 0x%X.", |
| HRESULT_CODE(GetLastError())); |
| } |
| } |
| |
| error = setObjectsLocked(true); |
| if (error.isError()) |
| { |
| return error; |
| } |
| |
| ASSERT(mFramebufferID != 0); |
| mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| mFunctionsGL->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, |
| mColorRenderbufferID); |
| |
| if (mDepthBufferFormat != GL_NONE) |
| { |
| ASSERT(mDepthRenderbufferID != 0); |
| mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDepthRenderbufferID); |
| mFunctionsGL->renderbufferStorage(GL_RENDERBUFFER, mDepthBufferFormat, |
| static_cast<GLsizei>(mWidth), |
| static_cast<GLsizei>(mHeight)); |
| |
| const gl::InternalFormat &depthStencilFormatInfo = |
| gl::GetInternalFormatInfo(mDepthBufferFormat); |
| if (depthStencilFormatInfo.depthBits > 0) |
| { |
| mFunctionsGL->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
| GL_RENDERBUFFER, mDepthRenderbufferID); |
| } |
| if (depthStencilFormatInfo.stencilBits > 0) |
| { |
| mFunctionsGL->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
| GL_RENDERBUFFER, mDepthRenderbufferID); |
| } |
| } |
| |
| mFirstSwap = true; |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| } // namespace rx |