|
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/SwapChain9.h" |
|
11 #include "libGLESv2/renderer/renderer9_utils.h" |
|
12 #include "libGLESv2/renderer/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 switch(interval) |
|
75 { |
|
76 case 0: return D3DPRESENT_INTERVAL_IMMEDIATE; |
|
77 case 1: return D3DPRESENT_INTERVAL_ONE; |
|
78 case 2: return D3DPRESENT_INTERVAL_TWO; |
|
79 case 3: return D3DPRESENT_INTERVAL_THREE; |
|
80 case 4: return D3DPRESENT_INTERVAL_FOUR; |
|
81 default: UNREACHABLE(); |
|
82 } |
|
83 |
|
84 return D3DPRESENT_INTERVAL_DEFAULT; |
|
85 } |
|
86 |
|
87 EGLint SwapChain9::resize(int backbufferWidth, int backbufferHeight) |
|
88 { |
|
89 // D3D9 does not support resizing swap chains without recreating them |
|
90 return reset(backbufferWidth, backbufferHeight, mSwapInterval); |
|
91 } |
|
92 |
|
93 EGLint SwapChain9::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval) |
|
94 { |
|
95 IDirect3DDevice9 *device = mRenderer->getDevice(); |
|
96 |
|
97 if (device == NULL) |
|
98 { |
|
99 return EGL_BAD_ACCESS; |
|
100 } |
|
101 |
|
102 // Evict all non-render target textures to system memory and release all resources |
|
103 // before reallocating them to free up as much video memory as possible. |
|
104 device->EvictManagedResources(); |
|
105 |
|
106 HRESULT result; |
|
107 |
|
108 // Release specific resources to free up memory for the new render target, while the |
|
109 // old render target still exists for the purpose of preserving its contents. |
|
110 if (mSwapChain) |
|
111 { |
|
112 mSwapChain->Release(); |
|
113 mSwapChain = NULL; |
|
114 } |
|
115 |
|
116 if (mBackBuffer) |
|
117 { |
|
118 mBackBuffer->Release(); |
|
119 mBackBuffer = NULL; |
|
120 } |
|
121 |
|
122 if (mOffscreenTexture) |
|
123 { |
|
124 mOffscreenTexture->Release(); |
|
125 mOffscreenTexture = NULL; |
|
126 } |
|
127 |
|
128 if (mDepthStencil) |
|
129 { |
|
130 mDepthStencil->Release(); |
|
131 mDepthStencil = NULL; |
|
132 } |
|
133 |
|
134 HANDLE *pShareHandle = NULL; |
|
135 if (!mWindow && mRenderer->getShareHandleSupport()) |
|
136 { |
|
137 pShareHandle = &mShareHandle; |
|
138 } |
|
139 |
|
140 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET, |
|
141 gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat), D3DPOOL_DEFAULT, |
|
142 &mOffscreenTexture, pShareHandle); |
|
143 if (FAILED(result)) |
|
144 { |
|
145 ERR("Could not create offscreen texture: %08lX", result); |
|
146 release(); |
|
147 |
|
148 if (d3d9::isDeviceLostError(result)) |
|
149 { |
|
150 return EGL_CONTEXT_LOST; |
|
151 } |
|
152 else |
|
153 { |
|
154 return EGL_BAD_ALLOC; |
|
155 } |
|
156 } |
|
157 |
|
158 IDirect3DSurface9 *oldRenderTarget = mRenderTarget; |
|
159 |
|
160 result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget); |
|
161 ASSERT(SUCCEEDED(result)); |
|
162 |
|
163 if (oldRenderTarget) |
|
164 { |
|
165 RECT rect = |
|
166 { |
|
167 0, 0, |
|
168 mWidth, mHeight |
|
169 }; |
|
170 |
|
171 if (rect.right > static_cast<LONG>(backbufferWidth)) |
|
172 { |
|
173 rect.right = backbufferWidth; |
|
174 } |
|
175 |
|
176 if (rect.bottom > static_cast<LONG>(backbufferHeight)) |
|
177 { |
|
178 rect.bottom = backbufferHeight; |
|
179 } |
|
180 |
|
181 mRenderer->endScene(); |
|
182 |
|
183 result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE); |
|
184 ASSERT(SUCCEEDED(result)); |
|
185 |
|
186 oldRenderTarget->Release(); |
|
187 } |
|
188 |
|
189 if (mWindow) |
|
190 { |
|
191 D3DPRESENT_PARAMETERS presentParameters = {0}; |
|
192 presentParameters.AutoDepthStencilFormat = gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat); |
|
193 presentParameters.BackBufferCount = 1; |
|
194 presentParameters.BackBufferFormat = gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat); |
|
195 presentParameters.EnableAutoDepthStencil = FALSE; |
|
196 presentParameters.Flags = 0; |
|
197 presentParameters.hDeviceWindow = mWindow; |
|
198 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented |
|
199 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented |
|
200 presentParameters.PresentationInterval = convertInterval(swapInterval); |
|
201 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD; |
|
202 presentParameters.Windowed = TRUE; |
|
203 presentParameters.BackBufferWidth = backbufferWidth; |
|
204 presentParameters.BackBufferHeight = backbufferHeight; |
|
205 |
|
206 // http://crbug.com/140239 |
|
207 // http://crbug.com/143434 |
|
208 // |
|
209 // Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width |
|
210 // when using the integrated Intel. This rounds the width up rather than down. |
|
211 // |
|
212 // Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. Therefore, when the vendor ID |
|
213 // is not Intel, the back buffer width must be exactly the same width as the window or horizontal scaling will occur. |
|
214 if (mRenderer->getAdapterVendor() == VENDOR_ID_INTEL) |
|
215 { |
|
216 presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64; |
|
217 } |
|
218 |
|
219 result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain); |
|
220 |
|
221 if (FAILED(result)) |
|
222 { |
|
223 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST); |
|
224 |
|
225 ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result); |
|
226 release(); |
|
227 |
|
228 if (d3d9::isDeviceLostError(result)) |
|
229 { |
|
230 return EGL_CONTEXT_LOST; |
|
231 } |
|
232 else |
|
233 { |
|
234 return EGL_BAD_ALLOC; |
|
235 } |
|
236 } |
|
237 |
|
238 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); |
|
239 ASSERT(SUCCEEDED(result)); |
|
240 InvalidateRect(mWindow, NULL, FALSE); |
|
241 } |
|
242 |
|
243 if (mDepthBufferFormat != GL_NONE) |
|
244 { |
|
245 result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight, |
|
246 gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat), |
|
247 D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL); |
|
248 |
|
249 if (FAILED(result)) |
|
250 { |
|
251 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL); |
|
252 |
|
253 ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result); |
|
254 release(); |
|
255 |
|
256 if (d3d9::isDeviceLostError(result)) |
|
257 { |
|
258 return EGL_CONTEXT_LOST; |
|
259 } |
|
260 else |
|
261 { |
|
262 return EGL_BAD_ALLOC; |
|
263 } |
|
264 } |
|
265 } |
|
266 |
|
267 mWidth = backbufferWidth; |
|
268 mHeight = backbufferHeight; |
|
269 mSwapInterval = swapInterval; |
|
270 |
|
271 return EGL_SUCCESS; |
|
272 } |
|
273 |
|
274 // parameters should be validated/clamped by caller |
|
275 EGLint SwapChain9::swapRect(EGLint x, EGLint y, EGLint width, 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(NULL); |
|
296 device->SetVertexShader(NULL); |
|
297 |
|
298 device->SetRenderTarget(0, mBackBuffer); |
|
299 device->SetDepthStencilSurface(NULL); |
|
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 D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f}; |
|
312 device->SetViewport(&viewport); |
|
313 |
|
314 float x1 = x - 0.5f; |
|
315 float y1 = (mHeight - y - height) - 0.5f; |
|
316 float x2 = (x + width) - 0.5f; |
|
317 float y2 = (mHeight - y) - 0.5f; |
|
318 |
|
319 float u1 = x / float(mWidth); |
|
320 float v1 = y / float(mHeight); |
|
321 float u2 = (x + width) / float(mWidth); |
|
322 float v2 = (y + height) / float(mHeight); |
|
323 |
|
324 float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2}, |
|
325 {x2, y1, 0.0f, 1.0f, u2, v2}, |
|
326 {x2, y2, 0.0f, 1.0f, u2, v1}, |
|
327 {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v |
|
328 |
|
329 mRenderer->startScene(); |
|
330 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float)); |
|
331 mRenderer->endScene(); |
|
332 |
|
333 device->SetTexture(0, NULL); |
|
334 |
|
335 RECT rect = |
|
336 { |
|
337 x, mHeight - y - height, |
|
338 x + width, mHeight - y |
|
339 }; |
|
340 |
|
341 HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0); |
|
342 |
|
343 mRenderer->markAllStateDirty(); |
|
344 |
|
345 if (d3d9::isDeviceLostError(result)) |
|
346 { |
|
347 return EGL_CONTEXT_LOST; |
|
348 } |
|
349 |
|
350 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR) |
|
351 { |
|
352 return EGL_BAD_ALLOC; |
|
353 } |
|
354 |
|
355 ASSERT(SUCCEEDED(result)); |
|
356 |
|
357 return EGL_SUCCESS; |
|
358 } |
|
359 |
|
360 // Increments refcount on surface. |
|
361 // caller must Release() the returned surface |
|
362 IDirect3DSurface9 *SwapChain9::getRenderTarget() |
|
363 { |
|
364 if (mRenderTarget) |
|
365 { |
|
366 mRenderTarget->AddRef(); |
|
367 } |
|
368 |
|
369 return mRenderTarget; |
|
370 } |
|
371 |
|
372 // Increments refcount on surface. |
|
373 // caller must Release() the returned surface |
|
374 IDirect3DSurface9 *SwapChain9::getDepthStencil() |
|
375 { |
|
376 if (mDepthStencil) |
|
377 { |
|
378 mDepthStencil->AddRef(); |
|
379 } |
|
380 |
|
381 return mDepthStencil; |
|
382 } |
|
383 |
|
384 // Increments refcount on texture. |
|
385 // caller must Release() the returned texture |
|
386 IDirect3DTexture9 *SwapChain9::getOffscreenTexture() |
|
387 { |
|
388 if (mOffscreenTexture) |
|
389 { |
|
390 mOffscreenTexture->AddRef(); |
|
391 } |
|
392 |
|
393 return mOffscreenTexture; |
|
394 } |
|
395 |
|
396 SwapChain9 *SwapChain9::makeSwapChain9(SwapChain *swapChain) |
|
397 { |
|
398 ASSERT(HAS_DYNAMIC_TYPE(rx::SwapChain9*, swapChain)); |
|
399 return static_cast<rx::SwapChain9*>(swapChain); |
|
400 } |
|
401 |
|
402 void SwapChain9::recreate() |
|
403 { |
|
404 if (!mSwapChain) |
|
405 { |
|
406 return; |
|
407 } |
|
408 |
|
409 IDirect3DDevice9 *device = mRenderer->getDevice(); |
|
410 if (device == NULL) |
|
411 { |
|
412 return; |
|
413 } |
|
414 |
|
415 D3DPRESENT_PARAMETERS presentParameters; |
|
416 HRESULT result = mSwapChain->GetPresentParameters(&presentParameters); |
|
417 ASSERT(SUCCEEDED(result)); |
|
418 |
|
419 IDirect3DSwapChain9* newSwapChain = NULL; |
|
420 result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain); |
|
421 if (FAILED(result)) |
|
422 { |
|
423 return; |
|
424 } |
|
425 |
|
426 mSwapChain->Release(); |
|
427 mSwapChain = newSwapChain; |
|
428 |
|
429 mBackBuffer->Release(); |
|
430 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); |
|
431 ASSERT(SUCCEEDED(result)); |
|
432 } |
|
433 |
|
434 } |