2
// Copyright (c) 2012-2014 The ANGLE Project Authors. All rights reserved.
3
// Use of this source code is governed by a BSD-style license that can be
4
// found in the LICENSE file.
7
// SwapChain9.cpp: Implements a back-end specific class for the D3D9 swap chain.
9
#include "libANGLE/renderer/d3d/d3d9/SwapChain9.h"
10
#include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h"
11
#include "libANGLE/renderer/d3d/d3d9/formatutils9.h"
12
#include "libANGLE/renderer/d3d/d3d9/Renderer9.h"
13
#include "libANGLE/features.h"
18
SwapChain9::SwapChain9(Renderer9 *renderer,
19
NativeWindow nativeWindow,
21
GLenum backBufferFormat,
22
GLenum depthBufferFormat,
24
: SwapChainD3D(nativeWindow, shareHandle, backBufferFormat, depthBufferFormat),
31
mRenderTarget(nullptr),
32
mDepthStencil(nullptr),
33
mOffscreenTexture(nullptr),
34
mColorRenderTarget(this, false),
35
mDepthStencilRenderTarget(this, true)
37
ASSERT(orientation == 0);
40
SwapChain9::~SwapChain9()
45
void SwapChain9::release()
47
SafeRelease(mSwapChain);
48
SafeRelease(mBackBuffer);
49
SafeRelease(mDepthStencil);
50
SafeRelease(mRenderTarget);
51
SafeRelease(mOffscreenTexture);
53
if (mNativeWindow.getNativeWindow())
59
static DWORD convertInterval(EGLint interval)
61
#if ANGLE_VSYNC == ANGLE_DISABLED
62
return D3DPRESENT_INTERVAL_IMMEDIATE;
66
case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
67
case 1: return D3DPRESENT_INTERVAL_ONE;
68
case 2: return D3DPRESENT_INTERVAL_TWO;
69
case 3: return D3DPRESENT_INTERVAL_THREE;
70
case 4: return D3DPRESENT_INTERVAL_FOUR;
71
default: UNREACHABLE();
74
return D3DPRESENT_INTERVAL_DEFAULT;
78
EGLint SwapChain9::resize(int backbufferWidth, int backbufferHeight)
80
// D3D9 does not support resizing swap chains without recreating them
81
return reset(backbufferWidth, backbufferHeight, mSwapInterval);
84
EGLint SwapChain9::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval)
86
IDirect3DDevice9 *device = mRenderer->getDevice();
90
return EGL_BAD_ACCESS;
93
// Evict all non-render target textures to system memory and release all resources
94
// before reallocating them to free up as much video memory as possible.
95
device->EvictManagedResources();
99
// Release specific resources to free up memory for the new render target, while the
100
// old render target still exists for the purpose of preserving its contents.
101
SafeRelease(mSwapChain);
102
SafeRelease(mBackBuffer);
103
SafeRelease(mOffscreenTexture);
104
SafeRelease(mDepthStencil);
106
HANDLE *pShareHandle = NULL;
107
if (!mNativeWindow.getNativeWindow() && mRenderer->getShareHandleSupport())
109
pShareHandle = &mShareHandle;
112
const d3d9::TextureFormat &backBufferd3dFormatInfo = d3d9::GetTextureFormatInfo(mOffscreenRenderTargetFormat);
113
result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
114
backBufferd3dFormatInfo.texFormat, D3DPOOL_DEFAULT, &mOffscreenTexture,
118
ERR("Could not create offscreen texture: %08lX", result);
121
if (d3d9::isDeviceLostError(result))
123
return EGL_CONTEXT_LOST;
127
return EGL_BAD_ALLOC;
131
IDirect3DSurface9 *oldRenderTarget = mRenderTarget;
133
result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
134
ASSERT(SUCCEEDED(result));
144
if (rect.right > static_cast<LONG>(backbufferWidth))
146
rect.right = backbufferWidth;
149
if (rect.bottom > static_cast<LONG>(backbufferHeight))
151
rect.bottom = backbufferHeight;
154
mRenderer->endScene();
156
result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE);
157
ASSERT(SUCCEEDED(result));
159
SafeRelease(oldRenderTarget);
162
const d3d9::TextureFormat &depthBufferd3dFormatInfo = d3d9::GetTextureFormatInfo(mDepthBufferFormat);
164
// Don't create a swapchain for NULLREF devices
165
D3DDEVTYPE deviceType = mRenderer->getD3D9DeviceType();
166
EGLNativeWindowType window = mNativeWindow.getNativeWindow();
167
if (window && deviceType != D3DDEVTYPE_NULLREF)
169
D3DPRESENT_PARAMETERS presentParameters = {0};
170
presentParameters.AutoDepthStencilFormat = depthBufferd3dFormatInfo.renderFormat;
171
presentParameters.BackBufferCount = 1;
172
presentParameters.BackBufferFormat = backBufferd3dFormatInfo.renderFormat;
173
presentParameters.EnableAutoDepthStencil = FALSE;
174
presentParameters.Flags = 0;
175
presentParameters.hDeviceWindow = window;
176
presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented
177
presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented
178
presentParameters.PresentationInterval = convertInterval(swapInterval);
179
presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
180
presentParameters.Windowed = TRUE;
181
presentParameters.BackBufferWidth = backbufferWidth;
182
presentParameters.BackBufferHeight = backbufferHeight;
184
// http://crbug.com/140239
185
// http://crbug.com/143434
187
// Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width
188
// when using the integrated Intel. This rounds the width up rather than down.
190
// Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. Therefore, when the vendor ID
191
// is not Intel, the back buffer width must be exactly the same width as the window or horizontal scaling will occur.
192
if (mRenderer->getVendorId() == VENDOR_ID_INTEL)
194
presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64;
197
result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
201
ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST);
203
ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
206
if (d3d9::isDeviceLostError(result))
208
return EGL_CONTEXT_LOST;
212
return EGL_BAD_ALLOC;
216
result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
217
ASSERT(SUCCEEDED(result));
218
InvalidateRect(window, NULL, FALSE);
221
if (mDepthBufferFormat != GL_NONE)
223
result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight,
224
depthBufferd3dFormatInfo.renderFormat,
225
D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL);
229
ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL);
231
ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result);
234
if (d3d9::isDeviceLostError(result))
236
return EGL_CONTEXT_LOST;
240
return EGL_BAD_ALLOC;
245
mWidth = backbufferWidth;
246
mHeight = backbufferHeight;
247
mSwapInterval = swapInterval;
252
// parameters should be validated/clamped by caller
253
EGLint SwapChain9::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
260
IDirect3DDevice9 *device = mRenderer->getDevice();
262
// Disable all pipeline operations
263
device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
264
device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
265
device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
266
device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
267
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
268
device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
269
device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
270
device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
271
device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
272
device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
273
device->SetPixelShader(NULL);
274
device->SetVertexShader(NULL);
276
device->SetRenderTarget(0, mBackBuffer);
277
device->SetDepthStencilSurface(NULL);
279
device->SetTexture(0, mOffscreenTexture);
280
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
281
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
282
device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
283
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
284
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
285
device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
286
device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
287
device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
289
for (UINT streamIndex = 0; streamIndex < gl::MAX_VERTEX_ATTRIBS; streamIndex++)
291
device->SetStreamSourceFreq(streamIndex, 1);
294
D3DVIEWPORT9 viewport = {0, 0, static_cast<DWORD>(mWidth), static_cast<DWORD>(mHeight), 0.0f, 1.0f};
295
device->SetViewport(&viewport);
298
float y1 = (mHeight - y - height) - 0.5f;
299
float x2 = (x + width) - 0.5f;
300
float y2 = (mHeight - y) - 0.5f;
302
float u1 = x / float(mWidth);
303
float v1 = y / float(mHeight);
304
float u2 = (x + width) / float(mWidth);
305
float v2 = (y + height) / float(mHeight);
307
float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2},
308
{x2, y1, 0.0f, 1.0f, u2, v2},
309
{x2, y2, 0.0f, 1.0f, u2, v1},
310
{x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v
312
mRenderer->startScene();
313
device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
314
mRenderer->endScene();
316
device->SetTexture(0, NULL);
320
static_cast<LONG>(x), static_cast<LONG>(mHeight - y - height),
321
static_cast<LONG>(x + width), static_cast<LONG>(mHeight - y)
324
HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0);
326
mRenderer->markAllStateDirty();
328
if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
330
return EGL_BAD_ALLOC;
333
// On Windows 8 systems, IDirect3DSwapChain9::Present sometimes returns 0x88760873 when the windows is
334
// in the process of entering/exiting fullscreen. This code doesn't seem to have any documentation. The
335
// device appears to be ok after emitting this error so simply return a failure to swap.
336
if (result == 0x88760873)
338
return EGL_BAD_MATCH;
341
// http://crbug.com/313210
342
// If our swap failed, trigger a device lost event. Resetting will work around an AMD-specific
343
// device removed bug with lost contexts when reinstalling drivers.
346
mRenderer->notifyDeviceLost();
347
return EGL_CONTEXT_LOST;
353
// Increments refcount on surface.
354
// caller must Release() the returned surface
355
// TODO: remove the AddRef to match SwapChain11
356
IDirect3DSurface9 *SwapChain9::getRenderTarget()
360
mRenderTarget->AddRef();
363
return mRenderTarget;
366
// Increments refcount on surface.
367
// caller must Release() the returned surface
368
// TODO: remove the AddRef to match SwapChain11
369
IDirect3DSurface9 *SwapChain9::getDepthStencil()
373
mDepthStencil->AddRef();
376
return mDepthStencil;
379
// Increments refcount on texture.
380
// caller must Release() the returned texture
381
// TODO: remove the AddRef to match SwapChain11
382
IDirect3DTexture9 *SwapChain9::getOffscreenTexture()
384
if (mOffscreenTexture)
386
mOffscreenTexture->AddRef();
389
return mOffscreenTexture;
392
void *SwapChain9::getKeyedMutex()
398
void SwapChain9::recreate()
405
IDirect3DDevice9 *device = mRenderer->getDevice();
411
D3DPRESENT_PARAMETERS presentParameters;
412
HRESULT result = mSwapChain->GetPresentParameters(&presentParameters);
413
ASSERT(SUCCEEDED(result));
415
IDirect3DSwapChain9* newSwapChain = NULL;
416
result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain);
422
SafeRelease(mSwapChain);
423
mSwapChain = newSwapChain;
425
SafeRelease(mBackBuffer);
426
result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
427
ASSERT(SUCCEEDED(result));