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