// // 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. // // D3DTextureSurfaceWGL.cpp: WGL implementation of egl::Surface for D3D texture interop. #include "libANGLE/renderer/gl/wgl/D3DTextureSurfaceWGL.h" #include "libANGLE/Surface.h" #include "libANGLE/renderer/d3d_format.h" #include "libANGLE/renderer/dxgi_format_map.h" #include "libANGLE/renderer/gl/FramebufferGL.h" #include "libANGLE/renderer/gl/RendererGL.h" #include "libANGLE/renderer/gl/StateManagerGL.h" #include "libANGLE/renderer/gl/TextureGL.h" #include "libANGLE/renderer/gl/wgl/DisplayWGL.h" #include "libANGLE/renderer/gl/wgl/FunctionsWGL.h" namespace rx { namespace { egl::Error GetD3D11TextureInfo(EGLenum buftype, ID3D11Texture2D *texture11, size_t *width, size_t *height, const angle::Format **angleFormat, IUnknown **object, IUnknown **device) { D3D11_TEXTURE2D_DESC textureDesc; texture11->GetDesc(&textureDesc); if (buftype == EGL_D3D_TEXTURE_ANGLE) { // From table egl.restrictions in EGL_ANGLE_d3d_texture_client_buffer. switch (textureDesc.Format) { case DXGI_FORMAT_R8G8B8A8_UNORM: case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: case DXGI_FORMAT_B8G8R8A8_UNORM: case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: case DXGI_FORMAT_R16G16B16A16_FLOAT: case DXGI_FORMAT_R32G32B32A32_FLOAT: break; default: SafeRelease(texture11); return egl::EglBadParameter() << "Unknown client buffer texture format: " << textureDesc.Format; } } ID3D11Device *d3d11Device = nullptr; texture11->GetDevice(&d3d11Device); if (d3d11Device == nullptr) { SafeRelease(texture11); return egl::EglBadParameter() << "Could not query the D3D11 device from the client buffer."; } if (angleFormat) { *angleFormat = &d3d11_angle::GetFormat(textureDesc.Format); } if (width) { *width = textureDesc.Width; } if (height) { *height = textureDesc.Height; } if (device) { *device = d3d11Device; } else { SafeRelease(d3d11Device); } if (object) { *object = texture11; } else { SafeRelease(texture11); } return egl::NoError(); } egl::Error GetD3D9TextureInfo(EGLenum buftype, IDirect3DTexture9 *texture9, size_t *width, size_t *height, const angle::Format **angleFormat, IUnknown **object, IUnknown **device) { D3DSURFACE_DESC surfaceDesc; if (FAILED(texture9->GetLevelDesc(0, &surfaceDesc))) { SafeRelease(texture9); return egl::EglBadParameter() << "Could not query description of the D3D9 surface."; } if (buftype == EGL_D3D_TEXTURE_ANGLE) { // From table egl.restrictions in EGL_ANGLE_d3d_texture_client_buffer. switch (surfaceDesc.Format) { case D3DFMT_R8G8B8: case D3DFMT_A8R8G8B8: case D3DFMT_A16B16G16R16F: case D3DFMT_A32B32G32R32F: break; default: SafeRelease(texture9); return egl::EglBadParameter() << "Unknown client buffer texture format: " << surfaceDesc.Format; } } if (angleFormat) { const auto &d3dFormatInfo = d3d9::GetD3DFormatInfo(surfaceDesc.Format); ASSERT(d3dFormatInfo.info().id != angle::FormatID::NONE); *angleFormat = &d3dFormatInfo.info(); } if (width) { *width = surfaceDesc.Width; } if (height) { *height = surfaceDesc.Height; } IDirect3DDevice9 *d3d9Device = nullptr; HRESULT result = texture9->GetDevice(&d3d9Device); if (FAILED(result)) { SafeRelease(texture9); return egl::EglBadParameter() << "Could not query the D3D9 device from the client buffer."; } if (device) { *device = d3d9Device; } else { SafeRelease(d3d9Device); } if (object) { *object = texture9; } else { SafeRelease(texture9); } return egl::NoError(); } egl::Error GetD3DTextureInfo(EGLenum buftype, EGLClientBuffer clientBuffer, ID3D11Device *d3d11Device, size_t *width, size_t *height, const angle::Format **angleFormat, IUnknown **object, IUnknown **device) { if (buftype == EGL_D3D_TEXTURE_ANGLE) { IUnknown *buffer = static_cast(clientBuffer); ID3D11Texture2D *texture11 = nullptr; IDirect3DTexture9 *texture9 = nullptr; if (SUCCEEDED(buffer->QueryInterface(&texture11))) { return GetD3D11TextureInfo(buftype, texture11, width, height, angleFormat, object, device); } else if (SUCCEEDED(buffer->QueryInterface(&texture9))) { return GetD3D9TextureInfo(buftype, texture9, width, height, angleFormat, object, device); } else { return egl::EglBadParameter() << "Provided buffer is not a IDirect3DTexture9 or ID3D11Texture2D."; } } else if (buftype == EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE) { ASSERT(d3d11Device); HANDLE shareHandle = static_cast(clientBuffer); ID3D11Texture2D *texture11 = nullptr; HRESULT result = d3d11Device->OpenSharedResource(shareHandle, __uuidof(ID3D11Texture2D), reinterpret_cast(&texture11)); if (FAILED(result)) { return egl::EglBadParameter() << "Failed to open share handle, " << gl::FmtHR(result); } return GetD3D11TextureInfo(buftype, texture11, width, height, angleFormat, object, device); } else { UNREACHABLE(); return egl::EglBadDisplay() << "Unknown buftype for D3DTextureSurfaceWGL."; } } } // anonymous namespace D3DTextureSurfaceWGL::D3DTextureSurfaceWGL(const egl::SurfaceState &state, StateManagerGL *stateManager, EGLenum buftype, EGLClientBuffer clientBuffer, DisplayWGL *display, HDC deviceContext, ID3D11Device *displayD3D11Device, const FunctionsGL *functionsGL, const FunctionsWGL *functionsWGL) : SurfaceWGL(state), mBuftype(buftype), mClientBuffer(clientBuffer), mDisplayD3D11Device(displayD3D11Device), mDisplay(display), mStateManager(stateManager), mFunctionsGL(functionsGL), mFunctionsWGL(functionsWGL), mDeviceContext(deviceContext), mWidth(0), mHeight(0), mColorFormat(nullptr), mDeviceHandle(nullptr), mObject(nullptr), mKeyedMutex(nullptr), mBoundObjectTextureHandle(nullptr), mBoundObjectRenderbufferHandle(nullptr), mColorRenderbufferID(0), mDepthStencilRenderbufferID(0) {} D3DTextureSurfaceWGL::~D3DTextureSurfaceWGL() { ASSERT(mBoundObjectTextureHandle == nullptr); SafeRelease(mObject); SafeRelease(mKeyedMutex); if (mDeviceHandle) { if (mBoundObjectRenderbufferHandle) { mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mBoundObjectRenderbufferHandle); mBoundObjectRenderbufferHandle = nullptr; } mStateManager->deleteRenderbuffer(mColorRenderbufferID); mStateManager->deleteRenderbuffer(mDepthStencilRenderbufferID); if (mBoundObjectTextureHandle) { mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mBoundObjectTextureHandle); mBoundObjectTextureHandle = nullptr; } mDisplay->releaseD3DDevice(mDeviceHandle); mDeviceHandle = nullptr; } } egl::Error D3DTextureSurfaceWGL::ValidateD3DTextureClientBuffer(EGLenum buftype, EGLClientBuffer clientBuffer, ID3D11Device *d3d11Device) { return GetD3DTextureInfo(buftype, clientBuffer, d3d11Device, nullptr, nullptr, nullptr, nullptr, nullptr); } egl::Error D3DTextureSurfaceWGL::initialize(const egl::Display *display) { IUnknown *device = nullptr; ANGLE_TRY(GetD3DTextureInfo(mBuftype, mClientBuffer, mDisplayD3D11Device, &mWidth, &mHeight, &mColorFormat, &mObject, &device)); if (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"; } } } // Grab the keyed mutex, if one exists mObject->QueryInterface(&mKeyedMutex); ASSERT(device != nullptr); egl::Error error = mDisplay->registerD3DDevice(device, &mDeviceHandle); SafeRelease(device); if (error.isError()) { return error; } mFunctionsGL->genRenderbuffers(1, &mColorRenderbufferID); mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mColorRenderbufferID); mBoundObjectRenderbufferHandle = mFunctionsWGL->dxRegisterObjectNV( mDeviceHandle, mObject, mColorRenderbufferID, GL_RENDERBUFFER, WGL_ACCESS_READ_WRITE_NV); if (mBoundObjectRenderbufferHandle == nullptr) { return egl::EglBadAlloc() << "Failed to register D3D object, " << gl::FmtErr(HRESULT_CODE(GetLastError())); } const egl::Config *config = mState.config; if (config->depthStencilFormat != GL_NONE) { mFunctionsGL->genRenderbuffers(1, &mDepthStencilRenderbufferID); mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDepthStencilRenderbufferID); mFunctionsGL->renderbufferStorage(GL_RENDERBUFFER, config->depthStencilFormat, static_cast(mWidth), static_cast(mHeight)); } return egl::NoError(); } egl::Error D3DTextureSurfaceWGL::makeCurrent(const gl::Context *context) { if (!mFunctionsWGL->dxLockObjectsNV(mDeviceHandle, 1, &mBoundObjectRenderbufferHandle)) { DWORD error = GetLastError(); return egl::EglBadAlloc() << "Failed to lock object, " << gl::FmtErr(HRESULT_CODE(error)); } return egl::NoError(); } egl::Error D3DTextureSurfaceWGL::unMakeCurrent(const gl::Context *context) { if (!mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, 1, &mBoundObjectRenderbufferHandle)) { DWORD error = GetLastError(); return egl::EglBadAlloc() << "Failed to unlock object, " << gl::FmtErr(HRESULT_CODE(error)); } return egl::NoError(); } egl::Error D3DTextureSurfaceWGL::swap(const gl::Context *context) { return egl::NoError(); } egl::Error D3DTextureSurfaceWGL::postSubBuffer(const gl::Context *context, EGLint x, EGLint y, EGLint width, EGLint height) { UNIMPLEMENTED(); return egl::NoError(); } egl::Error D3DTextureSurfaceWGL::querySurfacePointerANGLE(EGLint attribute, void **value) { switch (attribute) { case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE: *value = (mBuftype == EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE) ? mClientBuffer : nullptr; break; case EGL_DXGI_KEYED_MUTEX_ANGLE: *value = mKeyedMutex; break; default: UNREACHABLE(); } return egl::NoError(); } egl::Error D3DTextureSurfaceWGL::bindTexImage(const gl::Context *context, gl::Texture *texture, EGLint buffer) { ASSERT(mBoundObjectTextureHandle == nullptr); const TextureGL *textureGL = GetImplAs(texture); GLuint textureID = textureGL->getTextureID(); mBoundObjectTextureHandle = mFunctionsWGL->dxRegisterObjectNV( mDeviceHandle, mObject, textureID, GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV); if (mBoundObjectTextureHandle == nullptr) { DWORD error = GetLastError(); return egl::EglBadAlloc() << "Failed to register D3D object, " << gl::FmtErr(HRESULT_CODE(error)); } if (!mFunctionsWGL->dxLockObjectsNV(mDeviceHandle, 1, &mBoundObjectTextureHandle)) { DWORD error = GetLastError(); return egl::EglBadAlloc() << "Failed to lock object, " << gl::FmtErr(HRESULT_CODE(error)); } return egl::NoError(); } egl::Error D3DTextureSurfaceWGL::releaseTexImage(const gl::Context *context, EGLint buffer) { ASSERT(mBoundObjectTextureHandle != nullptr); if (!mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, 1, &mBoundObjectTextureHandle)) { DWORD error = GetLastError(); return egl::EglBadAlloc() << "Failed to unlock object, " << gl::FmtErr(HRESULT_CODE(error)); } if (!mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mBoundObjectTextureHandle)) { DWORD error = GetLastError(); return egl::EglBadAlloc() << "Failed to unregister D3D object, " << gl::FmtErr(HRESULT_CODE(error)); } mBoundObjectTextureHandle = nullptr; return egl::NoError(); } void D3DTextureSurfaceWGL::setSwapInterval(EGLint interval) { UNIMPLEMENTED(); } EGLint D3DTextureSurfaceWGL::getWidth() const { return static_cast(mWidth); } EGLint D3DTextureSurfaceWGL::getHeight() const { return static_cast(mHeight); } EGLint D3DTextureSurfaceWGL::isPostSubBufferSupported() const { return EGL_FALSE; } EGLint D3DTextureSurfaceWGL::getSwapBehavior() const { return EGL_BUFFER_PRESERVED; } FramebufferImpl *D3DTextureSurfaceWGL::createDefaultFramebuffer(const gl::Context *context, const gl::FramebufferState &data) { const FunctionsGL *functions = GetFunctionsGL(context); StateManagerGL *stateManager = GetStateManagerGL(context); GLuint framebufferID = 0; functions->genFramebuffers(1, &framebufferID); stateManager->bindFramebuffer(GL_FRAMEBUFFER, framebufferID); functions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mColorRenderbufferID); if (mState.config->depthSize > 0) { functions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepthStencilRenderbufferID); } if (mState.config->stencilSize > 0) { functions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepthStencilRenderbufferID); } return new FramebufferGL(data, framebufferID, true, false); } HDC D3DTextureSurfaceWGL::getDC() const { return mDeviceContext; } const angle::Format *D3DTextureSurfaceWGL::getD3DTextureColorFormat() const { return mColorFormat; } } // namespace rx