1 //
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.
5 //
6
7 // SwapChain9.cpp: Implements a back-end specific class for the D3D9 swap chain.
8
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/NativeWindow9.h"
13 #include "libANGLE/renderer/d3d/d3d9/Renderer9.h"
14 #include "libANGLE/features.h"
15
16 namespace rx
17 {
18
SwapChain9(Renderer9 * renderer,NativeWindow9 * nativeWindow,HANDLE shareHandle,IUnknown * d3dTexture,GLenum backBufferFormat,GLenum depthBufferFormat,EGLint orientation)19 SwapChain9::SwapChain9(Renderer9 *renderer,
20 NativeWindow9 *nativeWindow,
21 HANDLE shareHandle,
22 IUnknown *d3dTexture,
23 GLenum backBufferFormat,
24 GLenum depthBufferFormat,
25 EGLint orientation)
26 : SwapChainD3D(shareHandle, d3dTexture, backBufferFormat, depthBufferFormat),
27 mRenderer(renderer),
28 mWidth(-1),
29 mHeight(-1),
30 mSwapInterval(-1),
31 mNativeWindow(nativeWindow),
32 mSwapChain(nullptr),
33 mBackBuffer(nullptr),
34 mRenderTarget(nullptr),
35 mDepthStencil(nullptr),
36 mOffscreenTexture(nullptr),
37 mColorRenderTarget(this, false),
38 mDepthStencilRenderTarget(this, true)
39 {
40 ASSERT(orientation == 0);
41 }
42
~SwapChain9()43 SwapChain9::~SwapChain9()
44 {
45 release();
46 }
47
release()48 void SwapChain9::release()
49 {
50 SafeRelease(mSwapChain);
51 SafeRelease(mBackBuffer);
52 SafeRelease(mDepthStencil);
53 SafeRelease(mRenderTarget);
54 SafeRelease(mOffscreenTexture);
55
56 if (mNativeWindow->getNativeWindow())
57 {
58 mShareHandle = NULL;
59 }
60 }
61
convertInterval(EGLint interval)62 static DWORD convertInterval(EGLint interval)
63 {
64 #if ANGLE_VSYNC == ANGLE_DISABLED
65 return D3DPRESENT_INTERVAL_IMMEDIATE;
66 #else
67 switch(interval)
68 {
69 case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
70 case 1: return D3DPRESENT_INTERVAL_ONE;
71 case 2: return D3DPRESENT_INTERVAL_TWO;
72 case 3: return D3DPRESENT_INTERVAL_THREE;
73 case 4: return D3DPRESENT_INTERVAL_FOUR;
74 default: UNREACHABLE();
75 }
76
77 return D3DPRESENT_INTERVAL_DEFAULT;
78 #endif
79 }
80
resize(int backbufferWidth,int backbufferHeight)81 EGLint SwapChain9::resize(int backbufferWidth, int backbufferHeight)
82 {
83 // D3D9 does not support resizing swap chains without recreating them
84 return reset(backbufferWidth, backbufferHeight, mSwapInterval);
85 }
86
reset(int backbufferWidth,int backbufferHeight,EGLint swapInterval)87 EGLint SwapChain9::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval)
88 {
89 IDirect3DDevice9 *device = mRenderer->getDevice();
90
91 if (device == NULL)
92 {
93 return EGL_BAD_ACCESS;
94 }
95
96 // Evict all non-render target textures to system memory and release all resources
97 // before reallocating them to free up as much video memory as possible.
98 device->EvictManagedResources();
99
100 HRESULT result;
101
102 // Release specific resources to free up memory for the new render target, while the
103 // old render target still exists for the purpose of preserving its contents.
104 SafeRelease(mSwapChain);
105 SafeRelease(mBackBuffer);
106 SafeRelease(mOffscreenTexture);
107 SafeRelease(mDepthStencil);
108
109 const d3d9::TextureFormat &backBufferd3dFormatInfo =
110 d3d9::GetTextureFormatInfo(mOffscreenRenderTargetFormat);
111 if (mD3DTexture != nullptr)
112 {
113 result = mD3DTexture->QueryInterface(&mOffscreenTexture);
114 ASSERT(SUCCEEDED(result));
115 }
116 else
117 {
118 HANDLE *pShareHandle = NULL;
119 if (!mNativeWindow->getNativeWindow() && mRenderer->getShareHandleSupport())
120 {
121 pShareHandle = &mShareHandle;
122 }
123
124 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
125 backBufferd3dFormatInfo.texFormat, D3DPOOL_DEFAULT,
126 &mOffscreenTexture, pShareHandle);
127 if (FAILED(result))
128 {
129 ERR("Could not create offscreen texture: %08lX", result);
130 release();
131
132 if (d3d9::isDeviceLostError(result))
133 {
134 return EGL_CONTEXT_LOST;
135 }
136 else
137 {
138 return EGL_BAD_ALLOC;
139 }
140 }
141 }
142
143 IDirect3DSurface9 *oldRenderTarget = mRenderTarget;
144
145 result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
146 ASSERT(SUCCEEDED(result));
147
148 if (oldRenderTarget)
149 {
150 RECT rect =
151 {
152 0, 0,
153 mWidth, mHeight
154 };
155
156 if (rect.right > static_cast<LONG>(backbufferWidth))
157 {
158 rect.right = backbufferWidth;
159 }
160
161 if (rect.bottom > static_cast<LONG>(backbufferHeight))
162 {
163 rect.bottom = backbufferHeight;
164 }
165
166 mRenderer->endScene();
167
168 result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE);
169 ASSERT(SUCCEEDED(result));
170
171 SafeRelease(oldRenderTarget);
172 }
173
174 const d3d9::TextureFormat &depthBufferd3dFormatInfo = d3d9::GetTextureFormatInfo(mDepthBufferFormat);
175
176 // Don't create a swapchain for NULLREF devices
177 D3DDEVTYPE deviceType = mRenderer->getD3D9DeviceType();
178 EGLNativeWindowType window = mNativeWindow->getNativeWindow();
179 if (window && deviceType != D3DDEVTYPE_NULLREF)
180 {
181 D3DPRESENT_PARAMETERS presentParameters = {0};
182 presentParameters.AutoDepthStencilFormat = depthBufferd3dFormatInfo.renderFormat;
183 presentParameters.BackBufferCount = 1;
184 presentParameters.BackBufferFormat = backBufferd3dFormatInfo.renderFormat;
185 presentParameters.EnableAutoDepthStencil = FALSE;
186 presentParameters.Flags = 0;
187 presentParameters.hDeviceWindow = window;
188 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented
189 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented
190 presentParameters.PresentationInterval = convertInterval(swapInterval);
191 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
192 presentParameters.Windowed = TRUE;
193 presentParameters.BackBufferWidth = backbufferWidth;
194 presentParameters.BackBufferHeight = backbufferHeight;
195
196 // http://crbug.com/140239
197 // http://crbug.com/143434
198 //
199 // Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width
200 // when using the integrated Intel. This rounds the width up rather than down.
201 //
202 // Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. Therefore, when the vendor ID
203 // is not Intel, the back buffer width must be exactly the same width as the window or horizontal scaling will occur.
204 if (IsIntel(mRenderer->getVendorId()))
205 {
206 presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64;
207 }
208
209 result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
210
211 if (FAILED(result))
212 {
213 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST);
214
215 ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
216 release();
217
218 if (d3d9::isDeviceLostError(result))
219 {
220 return EGL_CONTEXT_LOST;
221 }
222 else
223 {
224 return EGL_BAD_ALLOC;
225 }
226 }
227
228 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
229 ASSERT(SUCCEEDED(result));
230 InvalidateRect(window, NULL, FALSE);
231 }
232
233 if (mDepthBufferFormat != GL_NONE)
234 {
235 result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight,
236 depthBufferd3dFormatInfo.renderFormat,
237 D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL);
238
239 if (FAILED(result))
240 {
241 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL);
242
243 ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result);
244 release();
245
246 if (d3d9::isDeviceLostError(result))
247 {
248 return EGL_CONTEXT_LOST;
249 }
250 else
251 {
252 return EGL_BAD_ALLOC;
253 }
254 }
255 }
256
257 mWidth = backbufferWidth;
258 mHeight = backbufferHeight;
259 mSwapInterval = swapInterval;
260
261 return EGL_SUCCESS;
262 }
263
264 // parameters should be validated/clamped by caller
swapRect(EGLint x,EGLint y,EGLint width,EGLint height)265 EGLint SwapChain9::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
266 {
267 if (!mSwapChain)
268 {
269 return EGL_SUCCESS;
270 }
271
272 IDirect3DDevice9 *device = mRenderer->getDevice();
273
274 // Disable all pipeline operations
275 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
276 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
277 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
278 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
279 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
280 device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
281 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
282 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
283 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
284 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
285 device->SetPixelShader(NULL);
286 device->SetVertexShader(NULL);
287
288 device->SetRenderTarget(0, mBackBuffer);
289 device->SetDepthStencilSurface(NULL);
290
291 device->SetTexture(0, mOffscreenTexture);
292 device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
293 device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
294 device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
295 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
296 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
297 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
298 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
299 device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
300
301 for (UINT streamIndex = 0; streamIndex < gl::MAX_VERTEX_ATTRIBS; streamIndex++)
302 {
303 device->SetStreamSourceFreq(streamIndex, 1);
304 }
305
306 D3DVIEWPORT9 viewport = {0, 0, static_cast<DWORD>(mWidth), static_cast<DWORD>(mHeight), 0.0f, 1.0f};
307 device->SetViewport(&viewport);
308
309 float x1 = x - 0.5f;
310 float y1 = (mHeight - y - height) - 0.5f;
311 float x2 = (x + width) - 0.5f;
312 float y2 = (mHeight - y) - 0.5f;
313
314 float u1 = x / float(mWidth);
315 float v1 = y / float(mHeight);
316 float u2 = (x + width) / float(mWidth);
317 float v2 = (y + height) / float(mHeight);
318
319 float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2},
320 {x2, y1, 0.0f, 1.0f, u2, v2},
321 {x2, y2, 0.0f, 1.0f, u2, v1},
322 {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v
323
324 mRenderer->startScene();
325 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
326 mRenderer->endScene();
327
328 device->SetTexture(0, NULL);
329
330 RECT rect =
331 {
332 static_cast<LONG>(x), static_cast<LONG>(mHeight - y - height),
333 static_cast<LONG>(x + width), static_cast<LONG>(mHeight - y)
334 };
335
336 HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0);
337
338 mRenderer->markAllStateDirty();
339
340 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
341 {
342 return EGL_BAD_ALLOC;
343 }
344
345 // On Windows 8 systems, IDirect3DSwapChain9::Present sometimes returns 0x88760873 when the windows is
346 // in the process of entering/exiting fullscreen. This code doesn't seem to have any documentation. The
347 // device appears to be ok after emitting this error so simply return a failure to swap.
348 if (result == static_cast<HRESULT>(0x88760873))
349 {
350 return EGL_BAD_MATCH;
351 }
352
353 // http://crbug.com/313210
354 // If our swap failed, trigger a device lost event. Resetting will work around an AMD-specific
355 // device removed bug with lost contexts when reinstalling drivers.
356 if (FAILED(result))
357 {
358 mRenderer->notifyDeviceLost();
359 return EGL_CONTEXT_LOST;
360 }
361
362 return EGL_SUCCESS;
363 }
364
365 // Increments refcount on surface.
366 // caller must Release() the returned surface
367 // TODO: remove the AddRef to match SwapChain11
getRenderTarget()368 IDirect3DSurface9 *SwapChain9::getRenderTarget()
369 {
370 if (mRenderTarget)
371 {
372 mRenderTarget->AddRef();
373 }
374
375 return mRenderTarget;
376 }
377
378 // Increments refcount on surface.
379 // caller must Release() the returned surface
380 // TODO: remove the AddRef to match SwapChain11
getDepthStencil()381 IDirect3DSurface9 *SwapChain9::getDepthStencil()
382 {
383 if (mDepthStencil)
384 {
385 mDepthStencil->AddRef();
386 }
387
388 return mDepthStencil;
389 }
390
391 // Increments refcount on texture.
392 // caller must Release() the returned texture
393 // TODO: remove the AddRef to match SwapChain11
getOffscreenTexture()394 IDirect3DTexture9 *SwapChain9::getOffscreenTexture()
395 {
396 if (mOffscreenTexture)
397 {
398 mOffscreenTexture->AddRef();
399 }
400
401 return mOffscreenTexture;
402 }
403
getKeyedMutex()404 void *SwapChain9::getKeyedMutex()
405 {
406 UNREACHABLE();
407 return nullptr;
408 }
409
recreate()410 void SwapChain9::recreate()
411 {
412 if (!mSwapChain)
413 {
414 return;
415 }
416
417 IDirect3DDevice9 *device = mRenderer->getDevice();
418 if (device == NULL)
419 {
420 return;
421 }
422
423 D3DPRESENT_PARAMETERS presentParameters;
424 HRESULT result = mSwapChain->GetPresentParameters(&presentParameters);
425 ASSERT(SUCCEEDED(result));
426
427 IDirect3DSwapChain9* newSwapChain = NULL;
428 result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain);
429 if (FAILED(result))
430 {
431 return;
432 }
433
434 SafeRelease(mSwapChain);
435 mSwapChain = newSwapChain;
436
437 SafeRelease(mBackBuffer);
438 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
439 ASSERT(SUCCEEDED(result));
440 }
441
442 }
443