| // | |
| // Copyright 2014 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. | |
| // | |
| // SurfaceD3D.cpp: D3D implementation of an EGL surface | |
| #include <Mfobjects.h> | |
| #include "libANGLE/renderer/d3d/SurfaceD3D.h" | |
| #include "libANGLE/Context.h" | |
| #include "libANGLE/Display.h" | |
| #include "libANGLE/Surface.h" | |
| #include "libANGLE/renderer/Format.h" | |
| #include "libANGLE/renderer/d3d/DisplayD3D.h" | |
| #include "libANGLE/renderer/d3d/RenderTargetD3D.h" | |
| #include "libANGLE/renderer/d3d/RendererD3D.h" | |
| #include "libANGLE/renderer/d3d/SwapChainD3D.h" | |
| #include "libANGLE/renderer/d3d/d3d11/formatutils11.h" | |
| #include <EGL/eglext.h> | |
| #include <tchar.h> | |
| #include <algorithm> | |
| // A key for ID3D11DeviceChild. The value should be a bool. | |
| // When set and true, indicates that the NV12 texture passed | |
| // in eglCreatePbufferFromClientBuffer EGL_D3D_TEXTURE_ANGLE should | |
| // draw it's chroma component when a ShaderResourceView is created for it. | |
| // If absent or false, the luma component is drawn. | |
| // | |
| // The value is fetched from ID3D11DeviceChild and stored before | |
| // eglCreatePbufferFromClientBuffer returns. | |
| // | |
| // {3C3A43AB-C69B-46C9-AA8D-B0CFFCD4596D} | |
| static const GUID kCobaltNv12BindChroma = {0x3c3a43ab, | |
| 0xc69b, | |
| 0x46c9, | |
| {0xaa, 0x8d, 0xb0, 0xcf, 0xfc, 0xd4, 0x59, 0x6d}}; | |
| namespace rx | |
| { | |
| SurfaceD3D::SurfaceD3D(const egl::SurfaceState &state, | |
| RendererD3D *renderer, | |
| egl::Display *display, | |
| EGLNativeWindowType window, | |
| EGLenum buftype, | |
| EGLClientBuffer clientBuffer, | |
| const egl::AttributeMap &attribs) | |
| : SurfaceImpl(state), | |
| mRenderer(renderer), | |
| mDisplay(display), | |
| mFixedSize(window == nullptr || attribs.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE) == EGL_TRUE), | |
| mFixedWidth(0), | |
| mFixedHeight(0), | |
| mOrientation(static_cast<EGLint>(attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0))), | |
| mRenderTargetFormat(state.config->renderTargetFormat), | |
| mDepthStencilFormat(state.config->depthStencilFormat), | |
| mColorFormat(nullptr), | |
| mSwapChain(nullptr), | |
| mSwapIntervalDirty(true), | |
| mNativeWindow(renderer->createNativeWindow(window, state.config, attribs)), | |
| mWidth(static_cast<EGLint>(attribs.get(EGL_WIDTH, 0))), | |
| mHeight(static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0))), | |
| mSwapInterval(1), | |
| mShareHandle(0), | |
| mD3DTexture(nullptr), | |
| mBuftype(buftype), | |
| mBindChroma(false) | |
| { | |
| if (window != nullptr && !mFixedSize) | |
| { | |
| mWidth = -1; | |
| mHeight = -1; | |
| } | |
| if (mFixedSize) | |
| { | |
| mFixedWidth = mWidth; | |
| mFixedHeight = mHeight; | |
| } | |
| switch (buftype) | |
| { | |
| case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE: | |
| mShareHandle = static_cast<HANDLE>(clientBuffer); | |
| break; | |
| case EGL_D3D_TEXTURE_ANGLE: | |
| { | |
| mD3DTexture = static_cast<IUnknown *>(clientBuffer); | |
| ASSERT(mD3DTexture != nullptr); | |
| mD3DTexture->AddRef(); | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| } | |
| SurfaceD3D::~SurfaceD3D() | |
| { | |
| releaseSwapChain(); | |
| SafeDelete(mNativeWindow); | |
| SafeRelease(mD3DTexture); | |
| } | |
| void SurfaceD3D::releaseSwapChain() | |
| { | |
| SafeDelete(mSwapChain); | |
| } | |
| egl::Error SurfaceD3D::initialize(const egl::Display *display) | |
| { | |
| if (mNativeWindow->getNativeWindow()) | |
| { | |
| if (!mNativeWindow->initialize()) | |
| { | |
| return egl::EglBadSurface(); | |
| } | |
| } | |
| if (mBuftype == EGL_D3D_TEXTURE_ANGLE) | |
| { | |
| UINT out; | |
| HRESULT hr = static_cast<ID3D11DeviceChild *>(mD3DTexture) | |
| ->GetPrivateData(kCobaltNv12BindChroma, &out, nullptr); | |
| mBindChroma = (SUCCEEDED(hr)) && (out != 0); | |
| ANGLE_TRY(mRenderer->getD3DTextureInfo(mState.config, mD3DTexture, mState.attributes, | |
| &mFixedWidth, &mFixedHeight, nullptr, nullptr, | |
| &mColorFormat)); | |
| if (mState.attributes.contains(EGL_GL_COLORSPACE)) | |
| { | |
| if (mColorFormat->id != angle::FormatID::R8G8B8A8_TYPELESS && | |
| mColorFormat->id != angle::FormatID::B8G8R8A8_TYPELESS) | |
| { | |
| return egl::EglBadMatch() | |
| << "EGL_GL_COLORSPACE may only be specified for TYPELESS textures"; | |
| } | |
| } | |
| if (mColorFormat->id == angle::FormatID::R8G8B8A8_TYPELESS) | |
| { | |
| EGLAttrib colorspace = | |
| mState.attributes.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_LINEAR); | |
| if (colorspace == EGL_GL_COLORSPACE_SRGB) | |
| { | |
| mColorFormat = &angle::Format::Get(angle::FormatID::R8G8B8A8_TYPELESS_SRGB); | |
| } | |
| } | |
| if (mColorFormat->id == angle::FormatID::B8G8R8A8_TYPELESS) | |
| { | |
| EGLAttrib colorspace = | |
| mState.attributes.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_LINEAR); | |
| if (colorspace == EGL_GL_COLORSPACE_SRGB) | |
| { | |
| mColorFormat = &angle::Format::Get(angle::FormatID::B8G8R8A8_TYPELESS_SRGB); | |
| } | |
| } | |
| mRenderTargetFormat = mColorFormat->fboImplementationInternalFormat; | |
| ID3D11Texture2D *d3Texture = static_cast<ID3D11Texture2D *>(mD3DTexture); | |
| D3D11_TEXTURE2D_DESC texture_desc; | |
| d3Texture->GetDesc(&texture_desc); | |
| if (texture_desc.Format == DXGI_FORMAT_NV12) | |
| { | |
| // NV12 textures cannot be rendered to, | |
| // so don't proceed to making a swap chain. | |
| mWidth = mFixedWidth; | |
| mHeight = mFixedHeight; | |
| return egl::NoError(); | |
| } | |
| } | |
| ANGLE_TRY(resetSwapChain(display)); | |
| return egl::NoError(); | |
| } | |
| FramebufferImpl *SurfaceD3D::createDefaultFramebuffer(const gl::Context *context, | |
| const gl::FramebufferState &data) | |
| { | |
| return mRenderer->createDefaultFramebuffer(data); | |
| } | |
| egl::Error SurfaceD3D::bindTexImage(const gl::Context *, gl::Texture *, EGLint) | |
| { | |
| return egl::NoError(); | |
| } | |
| egl::Error SurfaceD3D::releaseTexImage(const gl::Context *, EGLint) | |
| { | |
| return egl::NoError(); | |
| } | |
| egl::Error SurfaceD3D::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) | |
| { | |
| if (!mState.directComposition) | |
| { | |
| return egl::EglBadSurface() | |
| << "getSyncValues: surface requires Direct Composition to be enabled"; | |
| } | |
| return mSwapChain->getSyncValues(ust, msc, sbc); | |
| } | |
| egl::Error SurfaceD3D::resetSwapChain(const egl::Display *display) | |
| { | |
| ASSERT(!mSwapChain); | |
| int width; | |
| int height; | |
| if (!mFixedSize) | |
| { | |
| RECT windowRect; | |
| if (!mNativeWindow->getClientRect(&windowRect)) | |
| { | |
| ASSERT(false); | |
| return egl::EglBadSurface() << "Could not retrieve the window dimensions"; | |
| } | |
| width = windowRect.right - windowRect.left; | |
| height = windowRect.bottom - windowRect.top; | |
| } | |
| else | |
| { | |
| // non-window surface - size is determined at creation | |
| width = mFixedWidth; | |
| height = mFixedHeight; | |
| } | |
| mSwapChain = | |
| mRenderer->createSwapChain(mNativeWindow, mShareHandle, mD3DTexture, mRenderTargetFormat, | |
| mDepthStencilFormat, mOrientation, mState.config->samples); | |
| if (!mSwapChain) | |
| { | |
| return egl::EglBadAlloc(); | |
| } | |
| // This is a bit risky to pass the proxy context here, but it can happen at almost any time. | |
| DisplayD3D *displayD3D = GetImplAs<DisplayD3D>(display); | |
| egl::Error error = resetSwapChain(displayD3D, width, height); | |
| if (error.isError()) | |
| { | |
| SafeDelete(mSwapChain); | |
| return error; | |
| } | |
| return egl::NoError(); | |
| } | |
| egl::Error SurfaceD3D::resizeSwapChain(DisplayD3D *displayD3D, | |
| int backbufferWidth, | |
| int backbufferHeight) | |
| { | |
| ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0); | |
| ASSERT(mSwapChain); | |
| EGLint status = | |
| mSwapChain->resize(displayD3D, std::max(1, backbufferWidth), std::max(1, backbufferHeight)); | |
| if (status == EGL_CONTEXT_LOST) | |
| { | |
| mDisplay->notifyDeviceLost(); | |
| return egl::Error(status); | |
| } | |
| else if (status != EGL_SUCCESS) | |
| { | |
| return egl::Error(status); | |
| } | |
| mWidth = backbufferWidth; | |
| mHeight = backbufferHeight; | |
| return egl::NoError(); | |
| } | |
| egl::Error SurfaceD3D::resetSwapChain(DisplayD3D *displayD3D, | |
| int backbufferWidth, | |
| int backbufferHeight) | |
| { | |
| ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0); | |
| ASSERT(mSwapChain); | |
| EGLint status = mSwapChain->reset(displayD3D, std::max(1, backbufferWidth), | |
| std::max(1, backbufferHeight), mSwapInterval); | |
| if (status == EGL_CONTEXT_LOST) | |
| { | |
| mRenderer->notifyDeviceLost(); | |
| return egl::Error(status); | |
| } | |
| else if (status != EGL_SUCCESS) | |
| { | |
| return egl::Error(status); | |
| } | |
| mWidth = backbufferWidth; | |
| mHeight = backbufferHeight; | |
| mSwapIntervalDirty = false; | |
| return egl::NoError(); | |
| } | |
| egl::Error SurfaceD3D::swapRect(DisplayD3D *displayD3D, | |
| EGLint x, | |
| EGLint y, | |
| EGLint width, | |
| EGLint height) | |
| { | |
| if (!mSwapChain) | |
| { | |
| return egl::NoError(); | |
| } | |
| if (x + width > mWidth) | |
| { | |
| width = mWidth - x; | |
| } | |
| if (y + height > mHeight) | |
| { | |
| height = mHeight - y; | |
| } | |
| if (width != 0 && height != 0) | |
| { | |
| EGLint status = mSwapChain->swapRect(displayD3D, x, y, width, height); | |
| if (status == EGL_CONTEXT_LOST) | |
| { | |
| mRenderer->notifyDeviceLost(); | |
| return egl::Error(status); | |
| } | |
| else if (status != EGL_SUCCESS) | |
| { | |
| return egl::Error(status); | |
| } | |
| } | |
| ANGLE_TRY(checkForOutOfDateSwapChain(displayD3D)); | |
| return egl::NoError(); | |
| } | |
| egl::Error SurfaceD3D::checkForOutOfDateSwapChain(DisplayD3D *displayD3D) | |
| { | |
| RECT client; | |
| int clientWidth = getWidth(); | |
| int clientHeight = getHeight(); | |
| bool sizeDirty = false; | |
| if (!mFixedSize && !mNativeWindow->isIconic()) | |
| { | |
| // The window is automatically resized to 150x22 when it's minimized, but the swapchain | |
| // shouldn't be resized because that's not a useful size to render to. | |
| if (!mNativeWindow->getClientRect(&client)) | |
| { | |
| UNREACHABLE(); | |
| return egl::NoError(); | |
| } | |
| // Grow the buffer now, if the window has grown. We need to grow now to avoid losing | |
| // information. | |
| clientWidth = client.right - client.left; | |
| clientHeight = client.bottom - client.top; | |
| sizeDirty = clientWidth != getWidth() || clientHeight != getHeight(); | |
| } | |
| else if (mFixedSize) | |
| { | |
| clientWidth = mFixedWidth; | |
| clientHeight = mFixedHeight; | |
| sizeDirty = mFixedWidth != getWidth() || mFixedHeight != getHeight(); | |
| } | |
| if (mSwapIntervalDirty) | |
| { | |
| ANGLE_TRY(resetSwapChain(displayD3D, clientWidth, clientHeight)); | |
| } | |
| else if (sizeDirty) | |
| { | |
| ANGLE_TRY(resizeSwapChain(displayD3D, clientWidth, clientHeight)); | |
| } | |
| return egl::NoError(); | |
| } | |
| egl::Error SurfaceD3D::swap(const gl::Context *context) | |
| { | |
| DisplayD3D *displayD3D = GetImplAs<DisplayD3D>(context->getDisplay()); | |
| return swapRect(displayD3D, 0, 0, mWidth, mHeight); | |
| } | |
| egl::Error SurfaceD3D::postSubBuffer(const gl::Context *context, | |
| EGLint x, | |
| EGLint y, | |
| EGLint width, | |
| EGLint height) | |
| { | |
| DisplayD3D *displayD3D = GetImplAs<DisplayD3D>(context->getDisplay()); | |
| return swapRect(displayD3D, x, y, width, height); | |
| } | |
| rx::SwapChainD3D *SurfaceD3D::getSwapChain() const | |
| { | |
| return mSwapChain; | |
| } | |
| void SurfaceD3D::setSwapInterval(EGLint interval) | |
| { | |
| if (mSwapInterval == interval) | |
| { | |
| return; | |
| } | |
| mSwapInterval = interval; | |
| mSwapIntervalDirty = true; | |
| } | |
| void SurfaceD3D::setFixedWidth(EGLint width) | |
| { | |
| mFixedWidth = width; | |
| } | |
| void SurfaceD3D::setFixedHeight(EGLint height) | |
| { | |
| mFixedHeight = height; | |
| } | |
| EGLint SurfaceD3D::getWidth() const | |
| { | |
| return mWidth; | |
| } | |
| EGLint SurfaceD3D::getHeight() const | |
| { | |
| return mHeight; | |
| } | |
| EGLint SurfaceD3D::isPostSubBufferSupported() const | |
| { | |
| // post sub buffer is always possible on D3D surfaces | |
| return EGL_TRUE; | |
| } | |
| EGLint SurfaceD3D::getSwapBehavior() const | |
| { | |
| return EGL_BUFFER_PRESERVED; | |
| } | |
| egl::Error SurfaceD3D::querySurfacePointerANGLE(EGLint attribute, void **value) | |
| { | |
| if (attribute == EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE) | |
| { | |
| *value = mSwapChain->getShareHandle(); | |
| } | |
| else if (attribute == EGL_DXGI_KEYED_MUTEX_ANGLE) | |
| { | |
| *value = mSwapChain->getKeyedMutex(); | |
| } | |
| else | |
| UNREACHABLE(); | |
| return egl::NoError(); | |
| } | |
| const angle::Format *SurfaceD3D::getD3DTextureColorFormat() const | |
| { | |
| return mColorFormat; | |
| } | |
| angle::Result SurfaceD3D::getAttachmentRenderTarget(const gl::Context *context, | |
| GLenum binding, | |
| const gl::ImageIndex &imageIndex, | |
| GLsizei samples, | |
| FramebufferAttachmentRenderTarget **rtOut) | |
| { | |
| if (binding == GL_BACK) | |
| { | |
| *rtOut = mSwapChain->getColorRenderTarget(); | |
| } | |
| else | |
| { | |
| *rtOut = mSwapChain->getDepthStencilRenderTarget(); | |
| } | |
| return angle::Result::Continue; | |
| } | |
| angle::Result SurfaceD3D::initializeContents(const gl::Context *context, | |
| const gl::ImageIndex &imageIndex) | |
| { | |
| if (mState.config->renderTargetFormat != GL_NONE) | |
| { | |
| ANGLE_TRY(mRenderer->initRenderTarget(context, mSwapChain->getColorRenderTarget())); | |
| } | |
| if (mState.config->depthStencilFormat != GL_NONE) | |
| { | |
| ANGLE_TRY(mRenderer->initRenderTarget(context, mSwapChain->getDepthStencilRenderTarget())); | |
| } | |
| return angle::Result::Continue; | |
| } | |
| WindowSurfaceD3D::WindowSurfaceD3D(const egl::SurfaceState &state, | |
| RendererD3D *renderer, | |
| egl::Display *display, | |
| EGLNativeWindowType window, | |
| const egl::AttributeMap &attribs) | |
| : SurfaceD3D(state, renderer, display, window, 0, static_cast<EGLClientBuffer>(0), attribs) | |
| {} | |
| WindowSurfaceD3D::~WindowSurfaceD3D() {} | |
| PbufferSurfaceD3D::PbufferSurfaceD3D(const egl::SurfaceState &state, | |
| RendererD3D *renderer, | |
| egl::Display *display, | |
| EGLenum buftype, | |
| EGLClientBuffer clientBuffer, | |
| const egl::AttributeMap &attribs) | |
| : SurfaceD3D(state, | |
| renderer, | |
| display, | |
| static_cast<EGLNativeWindowType>(0), | |
| buftype, | |
| clientBuffer, | |
| attribs) | |
| {} | |
| PbufferSurfaceD3D::~PbufferSurfaceD3D() {} | |
| } // namespace rx |