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