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 == ANGLE_DISABLED
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