egl::Error DXGISwapChainWindowSurfaceWGL::swap()
{
    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::swap(const gl::Context *context)
{
    mFunctionsGL->flush();

    ANGLE_TRY(setObjectsLocked(false));

    HRESULT result = mSwapChain->Present(mSwapInterval, 0);
    mFirstSwap     = false;

    ANGLE_TRY(setObjectsLocked(true));

    if (FAILED(result))
    {
        return egl::EglBadAlloc() << "Failed to present swap chain, " << gl::FmtHR(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, &params);
    }

    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(const gl::Context *context,
                                                        EGLint x,
                                                        EGLint y,
                                                        EGLint width,
                                                        EGLint height)
{
    ASSERT(width > 0 && height > 0);
    ASSERT(mSwapChain1 != nullptr);

    mFunctionsGL->flush();

    ANGLE_TRY(setObjectsLocked(false));

    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, &params);
    }

    ANGLE_TRY(setObjectsLocked(true));

    if (FAILED(result))
    {
        return egl::EglBadAlloc() << "Failed to present swap chain, " << gl::FmtHR(result);
    }

    return checkForResize();
}
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);
}