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