blob: 7b9b32b412f3ea958e9e318f9cb4172aa4a193aa [file] [log] [blame]
#include "precompiled.h"
//
// Copyright (c) 2012-2013 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 "libGLESv2/renderer/SwapChain9.h"
#include "libGLESv2/renderer/renderer9_utils.h"
#include "libGLESv2/renderer/Renderer9.h"
namespace rx
{
SwapChain9::SwapChain9(Renderer9 *renderer, HWND window, HANDLE shareHandle,
GLenum backBufferFormat, GLenum depthBufferFormat)
: mRenderer(renderer), SwapChain(window, shareHandle, backBufferFormat, depthBufferFormat)
{
mSwapChain = NULL;
mBackBuffer = NULL;
mDepthStencil = NULL;
mRenderTarget = NULL;
mOffscreenTexture = NULL;
mWidth = -1;
mHeight = -1;
mSwapInterval = -1;
}
SwapChain9::~SwapChain9()
{
release();
}
void SwapChain9::release()
{
if (mSwapChain)
{
mSwapChain->Release();
mSwapChain = NULL;
}
if (mBackBuffer)
{
mBackBuffer->Release();
mBackBuffer = NULL;
}
if (mDepthStencil)
{
mDepthStencil->Release();
mDepthStencil = NULL;
}
if (mRenderTarget)
{
mRenderTarget->Release();
mRenderTarget = NULL;
}
if (mOffscreenTexture)
{
mOffscreenTexture->Release();
mOffscreenTexture = NULL;
}
if (mWindow)
mShareHandle = NULL;
}
static DWORD convertInterval(EGLint interval)
{
#if ANGLE_FORCE_VSYNC_OFF
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(int backbufferWidth, int backbufferHeight)
{
// D3D9 does not support resizing swap chains without recreating them
return reset(backbufferWidth, backbufferHeight, mSwapInterval);
}
EGLint SwapChain9::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval)
{
IDirect3DDevice9 *device = mRenderer->getDevice();
if (device == NULL)
{
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.
if (mSwapChain)
{
mSwapChain->Release();
mSwapChain = NULL;
}
if (mBackBuffer)
{
mBackBuffer->Release();
mBackBuffer = NULL;
}
if (mOffscreenTexture)
{
mOffscreenTexture->Release();
mOffscreenTexture = NULL;
}
if (mDepthStencil)
{
mDepthStencil->Release();
mDepthStencil = NULL;
}
HANDLE *pShareHandle = NULL;
if (!mWindow && mRenderer->getShareHandleSupport())
{
pShareHandle = &mShareHandle;
}
result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat), D3DPOOL_DEFAULT,
&mOffscreenTexture, pShareHandle);
if (FAILED(result))
{
ERR("Could not create offscreen texture: %08lX", 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));
oldRenderTarget->Release();
}
if (mWindow)
{
D3DPRESENT_PARAMETERS presentParameters = {0};
presentParameters.AutoDepthStencilFormat = gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat);
presentParameters.BackBufferCount = 1;
presentParameters.BackBufferFormat = gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat);
presentParameters.EnableAutoDepthStencil = FALSE;
presentParameters.Flags = 0;
presentParameters.hDeviceWindow = mWindow;
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 (mRenderer->getAdapterVendor() == VENDOR_ID_INTEL)
{
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: %08lX", 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(mWindow, NULL, FALSE);
}
if (mDepthBufferFormat != GL_NONE)
{
result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight,
gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat),
D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL);
if (FAILED(result))
{
ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL);
ERR("Could not create depthstencil surface for new swap chain: 0x%08X", 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(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(NULL);
device->SetVertexShader(NULL);
device->SetRenderTarget(0, mBackBuffer);
device->SetDepthStencilSurface(NULL);
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, mWidth, 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, NULL);
RECT rect =
{
x, mHeight - y - height,
x + width, mHeight - y
};
HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0);
mRenderer->markAllStateDirty();
if (d3d9::isDeviceLostError(result))
{
return EGL_CONTEXT_LOST;
}
if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
{
return EGL_BAD_ALLOC;
}
ASSERT(SUCCEEDED(result));
return EGL_SUCCESS;
}
// Increments refcount on surface.
// caller must Release() the returned surface
IDirect3DSurface9 *SwapChain9::getRenderTarget()
{
if (mRenderTarget)
{
mRenderTarget->AddRef();
}
return mRenderTarget;
}
// Increments refcount on surface.
// caller must Release() the returned surface
IDirect3DSurface9 *SwapChain9::getDepthStencil()
{
if (mDepthStencil)
{
mDepthStencil->AddRef();
}
return mDepthStencil;
}
// Increments refcount on texture.
// caller must Release() the returned texture
IDirect3DTexture9 *SwapChain9::getOffscreenTexture()
{
if (mOffscreenTexture)
{
mOffscreenTexture->AddRef();
}
return mOffscreenTexture;
}
SwapChain9 *SwapChain9::makeSwapChain9(SwapChain *swapChain)
{
ASSERT(HAS_DYNAMIC_TYPE(rx::SwapChain9*, swapChain));
return static_cast<rx::SwapChain9*>(swapChain);
}
void SwapChain9::recreate()
{
if (!mSwapChain)
{
return;
}
IDirect3DDevice9 *device = mRenderer->getDevice();
if (device == NULL)
{
return;
}
D3DPRESENT_PARAMETERS presentParameters;
HRESULT result = mSwapChain->GetPresentParameters(&presentParameters);
ASSERT(SUCCEEDED(result));
IDirect3DSwapChain9* newSwapChain = NULL;
result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain);
if (FAILED(result))
{
return;
}
mSwapChain->Release();
mSwapChain = newSwapChain;
mBackBuffer->Release();
result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
ASSERT(SUCCEEDED(result));
}
}