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