michael@0: #include "precompiled.h" michael@0: // michael@0: // Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: // SwapChain9.cpp: Implements a back-end specific class for the D3D9 swap chain. michael@0: michael@0: #include "libGLESv2/renderer/SwapChain9.h" michael@0: #include "libGLESv2/renderer/renderer9_utils.h" michael@0: #include "libGLESv2/renderer/Renderer9.h" michael@0: michael@0: namespace rx michael@0: { michael@0: michael@0: SwapChain9::SwapChain9(Renderer9 *renderer, HWND window, HANDLE shareHandle, michael@0: GLenum backBufferFormat, GLenum depthBufferFormat) michael@0: : mRenderer(renderer), SwapChain(window, shareHandle, backBufferFormat, depthBufferFormat) michael@0: { michael@0: mSwapChain = NULL; michael@0: mBackBuffer = NULL; michael@0: mDepthStencil = NULL; michael@0: mRenderTarget = NULL; michael@0: mOffscreenTexture = NULL; michael@0: mWidth = -1; michael@0: mHeight = -1; michael@0: mSwapInterval = -1; michael@0: } michael@0: michael@0: SwapChain9::~SwapChain9() michael@0: { michael@0: release(); michael@0: } michael@0: michael@0: void SwapChain9::release() michael@0: { michael@0: if (mSwapChain) michael@0: { michael@0: mSwapChain->Release(); michael@0: mSwapChain = NULL; michael@0: } michael@0: michael@0: if (mBackBuffer) michael@0: { michael@0: mBackBuffer->Release(); michael@0: mBackBuffer = NULL; michael@0: } michael@0: michael@0: if (mDepthStencil) michael@0: { michael@0: mDepthStencil->Release(); michael@0: mDepthStencil = NULL; michael@0: } michael@0: michael@0: if (mRenderTarget) michael@0: { michael@0: mRenderTarget->Release(); michael@0: mRenderTarget = NULL; michael@0: } michael@0: michael@0: if (mOffscreenTexture) michael@0: { michael@0: mOffscreenTexture->Release(); michael@0: mOffscreenTexture = NULL; michael@0: } michael@0: michael@0: if (mWindow) michael@0: mShareHandle = NULL; michael@0: } michael@0: michael@0: static DWORD convertInterval(EGLint interval) michael@0: { michael@0: switch(interval) michael@0: { michael@0: case 0: return D3DPRESENT_INTERVAL_IMMEDIATE; michael@0: case 1: return D3DPRESENT_INTERVAL_ONE; michael@0: case 2: return D3DPRESENT_INTERVAL_TWO; michael@0: case 3: return D3DPRESENT_INTERVAL_THREE; michael@0: case 4: return D3DPRESENT_INTERVAL_FOUR; michael@0: default: UNREACHABLE(); michael@0: } michael@0: michael@0: return D3DPRESENT_INTERVAL_DEFAULT; michael@0: } michael@0: michael@0: EGLint SwapChain9::resize(int backbufferWidth, int backbufferHeight) michael@0: { michael@0: // D3D9 does not support resizing swap chains without recreating them michael@0: return reset(backbufferWidth, backbufferHeight, mSwapInterval); michael@0: } michael@0: michael@0: EGLint SwapChain9::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval) michael@0: { michael@0: IDirect3DDevice9 *device = mRenderer->getDevice(); michael@0: michael@0: if (device == NULL) michael@0: { michael@0: return EGL_BAD_ACCESS; michael@0: } michael@0: michael@0: // Evict all non-render target textures to system memory and release all resources michael@0: // before reallocating them to free up as much video memory as possible. michael@0: device->EvictManagedResources(); michael@0: michael@0: HRESULT result; michael@0: michael@0: // Release specific resources to free up memory for the new render target, while the michael@0: // old render target still exists for the purpose of preserving its contents. michael@0: if (mSwapChain) michael@0: { michael@0: mSwapChain->Release(); michael@0: mSwapChain = NULL; michael@0: } michael@0: michael@0: if (mBackBuffer) michael@0: { michael@0: mBackBuffer->Release(); michael@0: mBackBuffer = NULL; michael@0: } michael@0: michael@0: if (mOffscreenTexture) michael@0: { michael@0: mOffscreenTexture->Release(); michael@0: mOffscreenTexture = NULL; michael@0: } michael@0: michael@0: if (mDepthStencil) michael@0: { michael@0: mDepthStencil->Release(); michael@0: mDepthStencil = NULL; michael@0: } michael@0: michael@0: HANDLE *pShareHandle = NULL; michael@0: if (!mWindow && mRenderer->getShareHandleSupport()) michael@0: { michael@0: pShareHandle = &mShareHandle; michael@0: } michael@0: michael@0: result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET, michael@0: gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat), D3DPOOL_DEFAULT, michael@0: &mOffscreenTexture, pShareHandle); michael@0: if (FAILED(result)) michael@0: { michael@0: ERR("Could not create offscreen texture: %08lX", result); michael@0: release(); michael@0: michael@0: if (d3d9::isDeviceLostError(result)) michael@0: { michael@0: return EGL_CONTEXT_LOST; michael@0: } michael@0: else michael@0: { michael@0: return EGL_BAD_ALLOC; michael@0: } michael@0: } michael@0: michael@0: IDirect3DSurface9 *oldRenderTarget = mRenderTarget; michael@0: michael@0: result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget); michael@0: ASSERT(SUCCEEDED(result)); michael@0: michael@0: if (oldRenderTarget) michael@0: { michael@0: RECT rect = michael@0: { michael@0: 0, 0, michael@0: mWidth, mHeight michael@0: }; michael@0: michael@0: if (rect.right > static_cast(backbufferWidth)) michael@0: { michael@0: rect.right = backbufferWidth; michael@0: } michael@0: michael@0: if (rect.bottom > static_cast(backbufferHeight)) michael@0: { michael@0: rect.bottom = backbufferHeight; michael@0: } michael@0: michael@0: mRenderer->endScene(); michael@0: michael@0: result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE); michael@0: ASSERT(SUCCEEDED(result)); michael@0: michael@0: oldRenderTarget->Release(); michael@0: } michael@0: michael@0: if (mWindow) michael@0: { michael@0: D3DPRESENT_PARAMETERS presentParameters = {0}; michael@0: presentParameters.AutoDepthStencilFormat = gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat); michael@0: presentParameters.BackBufferCount = 1; michael@0: presentParameters.BackBufferFormat = gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat); michael@0: presentParameters.EnableAutoDepthStencil = FALSE; michael@0: presentParameters.Flags = 0; michael@0: presentParameters.hDeviceWindow = mWindow; michael@0: presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented michael@0: presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented michael@0: presentParameters.PresentationInterval = convertInterval(swapInterval); michael@0: presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD; michael@0: presentParameters.Windowed = TRUE; michael@0: presentParameters.BackBufferWidth = backbufferWidth; michael@0: presentParameters.BackBufferHeight = backbufferHeight; michael@0: michael@0: // http://crbug.com/140239 michael@0: // http://crbug.com/143434 michael@0: // michael@0: // Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width michael@0: // when using the integrated Intel. This rounds the width up rather than down. michael@0: // michael@0: // Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. Therefore, when the vendor ID michael@0: // is not Intel, the back buffer width must be exactly the same width as the window or horizontal scaling will occur. michael@0: if (mRenderer->getAdapterVendor() == VENDOR_ID_INTEL) michael@0: { michael@0: presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64; michael@0: } michael@0: michael@0: result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain); michael@0: michael@0: if (FAILED(result)) michael@0: { michael@0: ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST); michael@0: michael@0: ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result); michael@0: release(); michael@0: michael@0: if (d3d9::isDeviceLostError(result)) michael@0: { michael@0: return EGL_CONTEXT_LOST; michael@0: } michael@0: else michael@0: { michael@0: return EGL_BAD_ALLOC; michael@0: } michael@0: } michael@0: michael@0: result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); michael@0: ASSERT(SUCCEEDED(result)); michael@0: InvalidateRect(mWindow, NULL, FALSE); michael@0: } michael@0: michael@0: if (mDepthBufferFormat != GL_NONE) michael@0: { michael@0: result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight, michael@0: gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat), michael@0: D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL); michael@0: michael@0: if (FAILED(result)) michael@0: { michael@0: ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL); michael@0: michael@0: ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result); michael@0: release(); michael@0: michael@0: if (d3d9::isDeviceLostError(result)) michael@0: { michael@0: return EGL_CONTEXT_LOST; michael@0: } michael@0: else michael@0: { michael@0: return EGL_BAD_ALLOC; michael@0: } michael@0: } michael@0: } michael@0: michael@0: mWidth = backbufferWidth; michael@0: mHeight = backbufferHeight; michael@0: mSwapInterval = swapInterval; michael@0: michael@0: return EGL_SUCCESS; michael@0: } michael@0: michael@0: // parameters should be validated/clamped by caller michael@0: EGLint SwapChain9::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) michael@0: { michael@0: if (!mSwapChain) michael@0: { michael@0: return EGL_SUCCESS; michael@0: } michael@0: michael@0: IDirect3DDevice9 *device = mRenderer->getDevice(); michael@0: michael@0: // Disable all pipeline operations michael@0: device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); michael@0: device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); michael@0: device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); michael@0: device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); michael@0: device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); michael@0: device->SetRenderState(D3DRS_STENCILENABLE, FALSE); michael@0: device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0); michael@0: device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED); michael@0: device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE); michael@0: device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); michael@0: device->SetPixelShader(NULL); michael@0: device->SetVertexShader(NULL); michael@0: michael@0: device->SetRenderTarget(0, mBackBuffer); michael@0: device->SetDepthStencilSurface(NULL); michael@0: michael@0: device->SetTexture(0, mOffscreenTexture); michael@0: device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); michael@0: device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); michael@0: device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); michael@0: device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); michael@0: device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); michael@0: device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); michael@0: device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); michael@0: device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1); michael@0: michael@0: D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f}; michael@0: device->SetViewport(&viewport); michael@0: michael@0: float x1 = x - 0.5f; michael@0: float y1 = (mHeight - y - height) - 0.5f; michael@0: float x2 = (x + width) - 0.5f; michael@0: float y2 = (mHeight - y) - 0.5f; michael@0: michael@0: float u1 = x / float(mWidth); michael@0: float v1 = y / float(mHeight); michael@0: float u2 = (x + width) / float(mWidth); michael@0: float v2 = (y + height) / float(mHeight); michael@0: michael@0: float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2}, michael@0: {x2, y1, 0.0f, 1.0f, u2, v2}, michael@0: {x2, y2, 0.0f, 1.0f, u2, v1}, michael@0: {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v michael@0: michael@0: mRenderer->startScene(); michael@0: device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float)); michael@0: mRenderer->endScene(); michael@0: michael@0: device->SetTexture(0, NULL); michael@0: michael@0: RECT rect = michael@0: { michael@0: x, mHeight - y - height, michael@0: x + width, mHeight - y michael@0: }; michael@0: michael@0: HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0); michael@0: michael@0: mRenderer->markAllStateDirty(); michael@0: michael@0: if (d3d9::isDeviceLostError(result)) michael@0: { michael@0: return EGL_CONTEXT_LOST; michael@0: } michael@0: michael@0: if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR) michael@0: { michael@0: return EGL_BAD_ALLOC; michael@0: } michael@0: michael@0: ASSERT(SUCCEEDED(result)); michael@0: michael@0: return EGL_SUCCESS; michael@0: } michael@0: michael@0: // Increments refcount on surface. michael@0: // caller must Release() the returned surface michael@0: IDirect3DSurface9 *SwapChain9::getRenderTarget() michael@0: { michael@0: if (mRenderTarget) michael@0: { michael@0: mRenderTarget->AddRef(); michael@0: } michael@0: michael@0: return mRenderTarget; michael@0: } michael@0: michael@0: // Increments refcount on surface. michael@0: // caller must Release() the returned surface michael@0: IDirect3DSurface9 *SwapChain9::getDepthStencil() michael@0: { michael@0: if (mDepthStencil) michael@0: { michael@0: mDepthStencil->AddRef(); michael@0: } michael@0: michael@0: return mDepthStencil; michael@0: } michael@0: michael@0: // Increments refcount on texture. michael@0: // caller must Release() the returned texture michael@0: IDirect3DTexture9 *SwapChain9::getOffscreenTexture() michael@0: { michael@0: if (mOffscreenTexture) michael@0: { michael@0: mOffscreenTexture->AddRef(); michael@0: } michael@0: michael@0: return mOffscreenTexture; michael@0: } michael@0: michael@0: SwapChain9 *SwapChain9::makeSwapChain9(SwapChain *swapChain) michael@0: { michael@0: ASSERT(HAS_DYNAMIC_TYPE(rx::SwapChain9*, swapChain)); michael@0: return static_cast(swapChain); michael@0: } michael@0: michael@0: void SwapChain9::recreate() michael@0: { michael@0: if (!mSwapChain) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: IDirect3DDevice9 *device = mRenderer->getDevice(); michael@0: if (device == NULL) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: D3DPRESENT_PARAMETERS presentParameters; michael@0: HRESULT result = mSwapChain->GetPresentParameters(&presentParameters); michael@0: ASSERT(SUCCEEDED(result)); michael@0: michael@0: IDirect3DSwapChain9* newSwapChain = NULL; michael@0: result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain); michael@0: if (FAILED(result)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: mSwapChain->Release(); michael@0: mSwapChain = newSwapChain; michael@0: michael@0: mBackBuffer->Release(); michael@0: result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); michael@0: ASSERT(SUCCEEDED(result)); michael@0: } michael@0: michael@0: }