blob: 6a8906f0b5e4360cd876b202cde6164a7e759c7a [file] [log] [blame]
//
// Copyright (c) 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 "libANGLE/renderer/d3d/SurfaceD3D.h"
#include "libANGLE/Display.h"
#include "libANGLE/Surface.h"
#include "libANGLE/renderer/d3d/RendererD3D.h"
#include "libANGLE/renderer/d3d/RenderTargetD3D.h"
#include "libANGLE/renderer/d3d/SwapChainD3D.h"
#include <tchar.h>
#include <EGL/eglext.h>
#include <algorithm>
namespace rx
{
SurfaceD3D *SurfaceD3D::createOffscreen(RendererD3D *renderer, egl::Display *display, const egl::Config *config, EGLClientBuffer shareHandle,
EGLint width, EGLint height)
{
return new SurfaceD3D(renderer, display, config, width, height, EGL_TRUE, 0, EGL_FALSE,
shareHandle, NULL);
}
SurfaceD3D *SurfaceD3D::createFromWindow(RendererD3D *renderer,
egl::Display *display,
const egl::Config *config,
EGLNativeWindowType window,
EGLint fixedSize,
EGLint directComposition,
EGLint width,
EGLint height,
EGLint orientation)
{
return new SurfaceD3D(renderer, display, config, width, height, fixedSize, orientation,
directComposition, static_cast<EGLClientBuffer>(0), window);
}
SurfaceD3D::SurfaceD3D(RendererD3D *renderer,
egl::Display *display,
const egl::Config *config,
EGLint width,
EGLint height,
EGLint fixedSize,
EGLint orientation,
EGLint directComposition,
EGLClientBuffer shareHandle,
EGLNativeWindowType window)
: SurfaceImpl(),
mRenderer(renderer),
mDisplay(display),
mFixedSize(fixedSize == EGL_TRUE),
mOrientation(orientation),
mRenderTargetFormat(config->renderTargetFormat),
mDepthStencilFormat(config->depthStencilFormat),
mSwapChain(nullptr),
mSwapIntervalDirty(true),
mNativeWindow(window, config, directComposition),
mWidth(width),
mHeight(height),
mSwapInterval(1),
mShareHandle(reinterpret_cast<HANDLE *>(shareHandle))
{
}
SurfaceD3D::~SurfaceD3D()
{
releaseSwapChain();
}
void SurfaceD3D::releaseSwapChain()
{
SafeDelete(mSwapChain);
}
egl::Error SurfaceD3D::initialize()
{
if (mNativeWindow.getNativeWindow())
{
if (!mNativeWindow.initialize())
{
return egl::Error(EGL_BAD_SURFACE);
}
}
egl::Error error = resetSwapChain();
if (error.isError())
{
return error;
}
return egl::Error(EGL_SUCCESS);
}
FramebufferImpl *SurfaceD3D::createDefaultFramebuffer(const gl::Framebuffer::Data &data)
{
return mRenderer->createFramebuffer(data);
}
egl::Error SurfaceD3D::bindTexImage(gl::Texture *, EGLint)
{
return egl::Error(EGL_SUCCESS);
}
egl::Error SurfaceD3D::releaseTexImage(EGLint)
{
return egl::Error(EGL_SUCCESS);
}
egl::Error SurfaceD3D::resetSwapChain()
{
ASSERT(!mSwapChain);
int width;
int height;
if (!mFixedSize)
{
RECT windowRect;
if (!mNativeWindow.getClientRect(&windowRect))
{
ASSERT(false);
return egl::Error(EGL_BAD_SURFACE, "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 = mWidth;
height = mHeight;
}
mSwapChain = mRenderer->createSwapChain(mNativeWindow, mShareHandle, mRenderTargetFormat,
mDepthStencilFormat, mOrientation);
if (!mSwapChain)
{
return egl::Error(EGL_BAD_ALLOC);
}
egl::Error error = resetSwapChain(width, height);
if (error.isError())
{
SafeDelete(mSwapChain);
return error;
}
return egl::Error(EGL_SUCCESS);
}
egl::Error SurfaceD3D::resizeSwapChain(int backbufferWidth, int backbufferHeight)
{
ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
ASSERT(mSwapChain);
EGLint status = mSwapChain->resize(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::Error(EGL_SUCCESS);
}
egl::Error SurfaceD3D::resetSwapChain(int backbufferWidth, int backbufferHeight)
{
ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
ASSERT(mSwapChain);
EGLint status = mSwapChain->reset(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::Error(EGL_SUCCESS);
}
egl::Error SurfaceD3D::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
{
if (!mSwapChain)
{
return egl::Error(EGL_SUCCESS);
}
if (x + width > mWidth)
{
width = mWidth - x;
}
if (y + height > mHeight)
{
height = mHeight - y;
}
if (width != 0 && height != 0)
{
EGLint status = mSwapChain->swapRect(x, y, width, height);
if (status == EGL_CONTEXT_LOST)
{
mRenderer->notifyDeviceLost();
return egl::Error(status);
}
else if (status != EGL_SUCCESS)
{
return egl::Error(status);
}
}
checkForOutOfDateSwapChain();
return egl::Error(EGL_SUCCESS);
}
bool SurfaceD3D::checkForOutOfDateSwapChain()
{
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))
{
ASSERT(false);
return false;
}
// 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();
}
bool wasDirty = (mSwapIntervalDirty || sizeDirty);
if (mSwapIntervalDirty)
{
resetSwapChain(clientWidth, clientHeight);
}
else if (sizeDirty)
{
resizeSwapChain(clientWidth, clientHeight);
}
return wasDirty;
}
egl::Error SurfaceD3D::swap()
{
return swapRect(0, 0, mWidth, mHeight);
}
egl::Error SurfaceD3D::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
{
return swapRect(x, y, width, height);
}
rx::SwapChainD3D *SurfaceD3D::getSwapChain() const
{
return mSwapChain;
}
void SurfaceD3D::setSwapInterval(EGLint interval)
{
if (mSwapInterval == interval)
{
return;
}
mSwapInterval = interval;
mSwapIntervalDirty = true;
}
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::Error(EGL_SUCCESS);
}
gl::Error SurfaceD3D::getAttachmentRenderTarget(const gl::FramebufferAttachment::Target &target,
FramebufferAttachmentRenderTarget **rtOut)
{
if (target.binding() == GL_BACK)
{
*rtOut = mSwapChain->getColorRenderTarget();
}
else
{
*rtOut = mSwapChain->getDepthStencilRenderTarget();
}
return gl::Error(GL_NO_ERROR);
}
}