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