blob: dd2ea9eda35cb04aad8ea2077c07caf1fd136b1e [file] [log] [blame]
//
// 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/d3d11/formatutils11.h"
#include "libANGLE/renderer/d3d/d3d9/formatutils9.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<IUnknown *>(clientBuffer);
ID3D11Texture2D *texture11 = nullptr;
IDirect3DTexture9 *texture9 = nullptr;
if (SUCCEEDED(buffer->QueryInterface<ID3D11Texture2D>(&texture11)))
{
return GetD3D11TextureInfo(buftype, texture11, width, height, angleFormat, object,
device);
}
else if (SUCCEEDED(buffer->QueryInterface<IDirect3DTexture9>(&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<HANDLE>(clientBuffer);
ID3D11Texture2D *texture11 = nullptr;
HRESULT result = d3d11Device->OpenSharedResource(shareHandle, __uuidof(ID3D11Texture2D),
reinterpret_cast<void **>(&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<GLsizei>(mWidth),
static_cast<GLsizei>(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<TextureGL>(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<EGLint>(mWidth);
}
EGLint D3DTextureSurfaceWGL::getHeight() const
{
return static_cast<EGLint>(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