blob: c8abac7a57eb1bc1cab88f01aaf465780e571136 [file] [log] [blame]
//
// Copyright 2012 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.
//
// SwapChain9.cpp: Implements a back-end specific class for the D3D9 swap chain.
#include "libANGLE/renderer/d3d/d3d9/SwapChain9.h"
#include "libANGLE/features.h"
#include "libANGLE/renderer/d3d/d3d9/NativeWindow9.h"
#include "libANGLE/renderer/d3d/d3d9/Renderer9.h"
#include "libANGLE/renderer/d3d/d3d9/formatutils9.h"
#include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h"
namespace rx
{
SwapChain9::SwapChain9(Renderer9 *renderer,
NativeWindow9 *nativeWindow,
HANDLE shareHandle,
IUnknown *d3dTexture,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation)
: SwapChainD3D(shareHandle, d3dTexture, backBufferFormat, depthBufferFormat),
mRenderer(renderer),
mWidth(-1),
mHeight(-1),
mSwapInterval(-1),
mNativeWindow(nativeWindow),
mSwapChain(nullptr),
mBackBuffer(nullptr),
mRenderTarget(nullptr),
mDepthStencil(nullptr),
mOffscreenTexture(nullptr),
mColorRenderTarget(this, false),
mDepthStencilRenderTarget(this, true)
{
ASSERT(orientation == 0);
}
SwapChain9::~SwapChain9()
{
release();
}
void SwapChain9::release()
{
SafeRelease(mSwapChain);
SafeRelease(mBackBuffer);
SafeRelease(mDepthStencil);
SafeRelease(mRenderTarget);
SafeRelease(mOffscreenTexture);
if (mNativeWindow->getNativeWindow())
{
mShareHandle = nullptr;
}
}
static DWORD convertInterval(EGLint interval)
{
#if ANGLE_VSYNC == ANGLE_DISABLED
return D3DPRESENT_INTERVAL_IMMEDIATE;
#else
switch (interval)
{
case 0:
return D3DPRESENT_INTERVAL_IMMEDIATE;
case 1:
return D3DPRESENT_INTERVAL_ONE;
case 2:
return D3DPRESENT_INTERVAL_TWO;
case 3:
return D3DPRESENT_INTERVAL_THREE;
case 4:
return D3DPRESENT_INTERVAL_FOUR;
default:
UNREACHABLE();
}
return D3DPRESENT_INTERVAL_DEFAULT;
#endif
}
EGLint SwapChain9::resize(DisplayD3D *displayD3D, int backbufferWidth, int backbufferHeight)
{
// D3D9 does not support resizing swap chains without recreating them
return reset(displayD3D, backbufferWidth, backbufferHeight, mSwapInterval);
}
EGLint SwapChain9::reset(DisplayD3D *displayD3D,
int backbufferWidth,
int backbufferHeight,
EGLint swapInterval)
{
IDirect3DDevice9 *device = mRenderer->getDevice();
if (device == nullptr)
{
return EGL_BAD_ACCESS;
}
// Evict all non-render target textures to system memory and release all resources
// before reallocating them to free up as much video memory as possible.
device->EvictManagedResources();
HRESULT result;
// Release specific resources to free up memory for the new render target, while the
// old render target still exists for the purpose of preserving its contents.
SafeRelease(mSwapChain);
SafeRelease(mBackBuffer);
SafeRelease(mOffscreenTexture);
SafeRelease(mDepthStencil);
const d3d9::TextureFormat &backBufferd3dFormatInfo =
d3d9::GetTextureFormatInfo(mOffscreenRenderTargetFormat);
if (mD3DTexture != nullptr)
{
result = mD3DTexture->QueryInterface(&mOffscreenTexture);
ASSERT(SUCCEEDED(result));
}
else
{
HANDLE *pShareHandle = nullptr;
if (!mNativeWindow->getNativeWindow() && mRenderer->getShareHandleSupport())
{
pShareHandle = &mShareHandle;
}
result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
backBufferd3dFormatInfo.texFormat, D3DPOOL_DEFAULT,
&mOffscreenTexture, pShareHandle);
if (FAILED(result))
{
ERR() << "Could not create offscreen texture, " << gl::FmtHR(result);
release();
if (d3d9::isDeviceLostError(result))
{
return EGL_CONTEXT_LOST;
}
else
{
return EGL_BAD_ALLOC;
}
}
}
IDirect3DSurface9 *oldRenderTarget = mRenderTarget;
result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
ASSERT(SUCCEEDED(result));
if (oldRenderTarget)
{
RECT rect = {0, 0, mWidth, mHeight};
if (rect.right > static_cast<LONG>(backbufferWidth))
{
rect.right = backbufferWidth;
}
if (rect.bottom > static_cast<LONG>(backbufferHeight))
{
rect.bottom = backbufferHeight;
}
mRenderer->endScene();
result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE);
ASSERT(SUCCEEDED(result));
SafeRelease(oldRenderTarget);
}
const d3d9::TextureFormat &depthBufferd3dFormatInfo =
d3d9::GetTextureFormatInfo(mDepthBufferFormat);
// Don't create a swapchain for NULLREF devices
D3DDEVTYPE deviceType = mRenderer->getD3D9DeviceType();
EGLNativeWindowType window = mNativeWindow->getNativeWindow();
if (window && deviceType != D3DDEVTYPE_NULLREF)
{
D3DPRESENT_PARAMETERS presentParameters = {};
presentParameters.AutoDepthStencilFormat = depthBufferd3dFormatInfo.renderFormat;
presentParameters.BackBufferCount = 1;
presentParameters.BackBufferFormat = backBufferd3dFormatInfo.renderFormat;
presentParameters.EnableAutoDepthStencil = FALSE;
presentParameters.Flags = 0;
presentParameters.hDeviceWindow = window;
presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented
presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented
presentParameters.PresentationInterval = convertInterval(swapInterval);
presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
presentParameters.Windowed = TRUE;
presentParameters.BackBufferWidth = backbufferWidth;
presentParameters.BackBufferHeight = backbufferHeight;
// http://crbug.com/140239
// http://crbug.com/143434
//
// Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a
// multiple of 64 pixels in width when using the integrated Intel. This rounds the width up
// rather than down.
//
// Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present.
// Therefore, when the vendor ID is not Intel, the back buffer width must be exactly the
// same width as the window or horizontal scaling will occur.
if (IsIntel(mRenderer->getVendorId()))
{
presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64;
}
result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
if (FAILED(result))
{
ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY ||
result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST);
ERR() << "Could not create additional swap chains or offscreen surfaces, "
<< gl::FmtHR(result);
release();
if (d3d9::isDeviceLostError(result))
{
return EGL_CONTEXT_LOST;
}
else
{
return EGL_BAD_ALLOC;
}
}
result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
ASSERT(SUCCEEDED(result));
InvalidateRect(window, nullptr, FALSE);
}
if (mDepthBufferFormat != GL_NONE)
{
result = device->CreateDepthStencilSurface(
backbufferWidth, backbufferHeight, depthBufferd3dFormatInfo.renderFormat,
D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, nullptr);
if (FAILED(result))
{
ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY ||
result == D3DERR_INVALIDCALL);
ERR() << "Could not create depthstencil surface for new swap chain, "
<< gl::FmtHR(result);
release();
if (d3d9::isDeviceLostError(result))
{
return EGL_CONTEXT_LOST;
}
else
{
return EGL_BAD_ALLOC;
}
}
}
mWidth = backbufferWidth;
mHeight = backbufferHeight;
mSwapInterval = swapInterval;
return EGL_SUCCESS;
}
// parameters should be validated/clamped by caller
EGLint SwapChain9::swapRect(DisplayD3D *displayD3D, EGLint x, EGLint y, EGLint width, EGLint height)
{
if (!mSwapChain)
{
return EGL_SUCCESS;
}
IDirect3DDevice9 *device = mRenderer->getDevice();
// Disable all pipeline operations
device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
device->SetRenderState(D3DRS_COLORWRITEENABLE,
D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE |
D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
device->SetPixelShader(nullptr);
device->SetVertexShader(nullptr);
device->SetRenderTarget(0, mBackBuffer);
device->SetDepthStencilSurface(nullptr);
device->SetTexture(0, mOffscreenTexture);
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
for (UINT streamIndex = 0; streamIndex < gl::MAX_VERTEX_ATTRIBS; streamIndex++)
{
device->SetStreamSourceFreq(streamIndex, 1);
}
D3DVIEWPORT9 viewport = {0, 0, static_cast<DWORD>(mWidth), static_cast<DWORD>(mHeight),
0.0f, 1.0f};
device->SetViewport(&viewport);
float x1 = x - 0.5f;
float y1 = (mHeight - y - height) - 0.5f;
float x2 = (x + width) - 0.5f;
float y2 = (mHeight - y) - 0.5f;
float u1 = x / float(mWidth);
float v1 = y / float(mHeight);
float u2 = (x + width) / float(mWidth);
float v2 = (y + height) / float(mHeight);
float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2},
{x2, y1, 0.0f, 1.0f, u2, v2},
{x2, y2, 0.0f, 1.0f, u2, v1},
{x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v
mRenderer->startScene();
device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
mRenderer->endScene();
device->SetTexture(0, nullptr);
RECT rect = {static_cast<LONG>(x), static_cast<LONG>(mHeight - y - height),
static_cast<LONG>(x + width), static_cast<LONG>(mHeight - y)};
HRESULT result = mSwapChain->Present(&rect, &rect, nullptr, nullptr, 0);
mRenderer->markAllStateDirty();
if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY ||
result == D3DERR_DRIVERINTERNALERROR)
{
return EGL_BAD_ALLOC;
}
// On Windows 8 systems, IDirect3DSwapChain9::Present sometimes returns 0x88760873 when the
// windows is in the process of entering/exiting fullscreen. This code doesn't seem to have any
// documentation. The device appears to be ok after emitting this error so simply return a
// failure to swap.
if (result == static_cast<HRESULT>(0x88760873) || result == static_cast<HRESULT>(0x88760872))
{
return EGL_BAD_MATCH;
}
// http://crbug.com/313210
// If our swap failed, trigger a device lost event. Resetting will work around an AMD-specific
// device removed bug with lost contexts when reinstalling drivers.
if (FAILED(result))
{
mRenderer->notifyDeviceLost();
return EGL_CONTEXT_LOST;
}
return EGL_SUCCESS;
}
// Increments refcount on surface.
// caller must Release() the returned surface
// TODO: remove the AddRef to match SwapChain11
IDirect3DSurface9 *SwapChain9::getRenderTarget()
{
if (mRenderTarget)
{
mRenderTarget->AddRef();
}
return mRenderTarget;
}
// Increments refcount on surface.
// caller must Release() the returned surface
// TODO: remove the AddRef to match SwapChain11
IDirect3DSurface9 *SwapChain9::getDepthStencil()
{
if (mDepthStencil)
{
mDepthStencil->AddRef();
}
return mDepthStencil;
}
// Increments refcount on texture.
// caller must Release() the returned texture
// TODO: remove the AddRef to match SwapChain11
IDirect3DTexture9 *SwapChain9::getOffscreenTexture()
{
if (mOffscreenTexture)
{
mOffscreenTexture->AddRef();
}
return mOffscreenTexture;
}
void *SwapChain9::getKeyedMutex()
{
UNREACHABLE();
return nullptr;
}
egl::Error SwapChain9::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
{
UNREACHABLE();
return egl::EglBadSurface();
}
void SwapChain9::recreate()
{
if (!mSwapChain)
{
return;
}
IDirect3DDevice9 *device = mRenderer->getDevice();
if (device == nullptr)
{
return;
}
D3DPRESENT_PARAMETERS presentParameters;
HRESULT result = mSwapChain->GetPresentParameters(&presentParameters);
ASSERT(SUCCEEDED(result));
IDirect3DSwapChain9 *newSwapChain = nullptr;
result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain);
if (FAILED(result))
{
return;
}
SafeRelease(mSwapChain);
mSwapChain = newSwapChain;
SafeRelease(mBackBuffer);
result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
ASSERT(SUCCEEDED(result));
}
RenderTargetD3D *SwapChain9::getColorRenderTarget()
{
return &mColorRenderTarget;
}
RenderTargetD3D *SwapChain9::getDepthStencilRenderTarget()
{
return &mDepthStencilRenderTarget;
}
} // namespace rx