blob: 8b3223d099719d248c614095cace35ea91376c05 [file] [log] [blame]
daniel@transgaming.com3c720782012-10-31 18:42:34 +00001//
daniel@transgaming.comed36abd2013-01-11 21:15:58 +00002// Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
daniel@transgaming.com3c720782012-10-31 18:42:34 +00003// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +00007// SwapChain9.cpp: Implements a back-end specific class for the D3D9 swap chain.
daniel@transgaming.com3c720782012-10-31 18:42:34 +00008
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +00009#include "libGLESv2/renderer/SwapChain9.h"
daniel@transgaming.com3c720782012-10-31 18:42:34 +000010
11#include "common/debug.h"
12#include "libGLESv2/utilities.h"
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +000013#include "libGLESv2/renderer/renderer9_utils.h"
14#include "libGLESv2/renderer/Renderer9.h"
15#include "libGLESv2/renderer/RenderTarget9.h"
daniel@transgaming.com3c720782012-10-31 18:42:34 +000016#include "libGLESv2/Context.h"
daniel@transgaming.comcfa8efd2012-11-28 19:30:55 +000017#include "libGLESv2/main.h"
daniel@transgaming.com3c720782012-10-31 18:42:34 +000018
daniel@transgaming.com76d3e6e2012-10-31 19:55:33 +000019namespace rx
daniel@transgaming.com3c720782012-10-31 18:42:34 +000020{
21
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +000022SwapChain9::SwapChain9(Renderer9 *renderer, HWND window, HANDLE shareHandle,
23 GLenum backBufferFormat, GLenum depthBufferFormat)
24 : mRenderer(renderer), SwapChain(window, shareHandle, backBufferFormat, depthBufferFormat)
daniel@transgaming.com3c720782012-10-31 18:42:34 +000025{
26 mSwapChain = NULL;
27 mBackBuffer = NULL;
28 mDepthStencil = NULL;
29 mRenderTarget = NULL;
30 mOffscreenTexture = NULL;
31 mWidth = -1;
32 mHeight = -1;
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +000033 mSwapInterval = -1;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000034}
35
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +000036SwapChain9::~SwapChain9()
daniel@transgaming.com3c720782012-10-31 18:42:34 +000037{
38 release();
39}
40
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +000041void SwapChain9::release()
daniel@transgaming.com3c720782012-10-31 18:42:34 +000042{
43 if (mSwapChain)
44 {
45 mSwapChain->Release();
46 mSwapChain = NULL;
47 }
48
49 if (mBackBuffer)
50 {
51 mBackBuffer->Release();
52 mBackBuffer = NULL;
53 }
54
55 if (mDepthStencil)
56 {
57 mDepthStencil->Release();
58 mDepthStencil = NULL;
59 }
60
61 if (mRenderTarget)
62 {
63 mRenderTarget->Release();
64 mRenderTarget = NULL;
65 }
66
67 if (mOffscreenTexture)
68 {
69 mOffscreenTexture->Release();
70 mOffscreenTexture = NULL;
71 }
72
daniel@transgaming.com21cfaef2012-10-31 18:42:43 +000073 if (mWindow)
74 mShareHandle = NULL;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000075}
76
77static DWORD convertInterval(EGLint interval)
78{
79 switch(interval)
80 {
81 case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
82 case 1: return D3DPRESENT_INTERVAL_ONE;
83 case 2: return D3DPRESENT_INTERVAL_TWO;
84 case 3: return D3DPRESENT_INTERVAL_THREE;
85 case 4: return D3DPRESENT_INTERVAL_FOUR;
86 default: UNREACHABLE();
87 }
88
89 return D3DPRESENT_INTERVAL_DEFAULT;
90}
91
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +000092EGLint SwapChain9::resize(int backbufferWidth, int backbufferHeight)
93{
94 // D3D9 does not support resizing swap chains without recreating them
95 return reset(backbufferWidth, backbufferHeight, mSwapInterval);
96}
97
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +000098EGLint SwapChain9::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval)
daniel@transgaming.com3c720782012-10-31 18:42:34 +000099{
100 IDirect3DDevice9 *device = mRenderer->getDevice();
101
102 if (device == NULL)
103 {
104 return EGL_BAD_ACCESS;
105 }
106
107 // Evict all non-render target textures to system memory and release all resources
108 // before reallocating them to free up as much video memory as possible.
109 device->EvictManagedResources();
110
111 HRESULT result;
112
113 // Release specific resources to free up memory for the new render target, while the
114 // old render target still exists for the purpose of preserving its contents.
115 if (mSwapChain)
116 {
117 mSwapChain->Release();
118 mSwapChain = NULL;
119 }
120
121 if (mBackBuffer)
122 {
123 mBackBuffer->Release();
124 mBackBuffer = NULL;
125 }
126
127 if (mOffscreenTexture)
128 {
129 mOffscreenTexture->Release();
130 mOffscreenTexture = NULL;
131 }
132
133 if (mDepthStencil)
134 {
135 mDepthStencil->Release();
136 mDepthStencil = NULL;
137 }
138
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000139 HANDLE *pShareHandle = NULL;
140 if (!mWindow && mRenderer->getShareHandleSupport())
141 {
142 pShareHandle = &mShareHandle;
143 }
144
145 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
daniel@transgaming.com682a37c2012-11-28 19:34:44 +0000146 gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat), D3DPOOL_DEFAULT,
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000147 &mOffscreenTexture, pShareHandle);
148 if (FAILED(result))
149 {
150 ERR("Could not create offscreen texture: %08lX", result);
151 release();
152
shannon.woods@transgaming.comf5f59492013-02-28 23:04:40 +0000153 if (d3d9::isDeviceLostError(result))
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000154 {
155 return EGL_CONTEXT_LOST;
156 }
157 else
158 {
159 return EGL_BAD_ALLOC;
160 }
161 }
162
163 IDirect3DSurface9 *oldRenderTarget = mRenderTarget;
164
165 result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
166 ASSERT(SUCCEEDED(result));
167
168 if (oldRenderTarget)
169 {
170 RECT rect =
171 {
172 0, 0,
173 mWidth, mHeight
174 };
175
176 if (rect.right > static_cast<LONG>(backbufferWidth))
177 {
178 rect.right = backbufferWidth;
179 }
180
181 if (rect.bottom > static_cast<LONG>(backbufferHeight))
182 {
183 rect.bottom = backbufferHeight;
184 }
185
186 mRenderer->endScene();
187
188 result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE);
189 ASSERT(SUCCEEDED(result));
190
191 oldRenderTarget->Release();
192 }
193
194 if (mWindow)
195 {
196 D3DPRESENT_PARAMETERS presentParameters = {0};
daniel@transgaming.com682a37c2012-11-28 19:34:44 +0000197 presentParameters.AutoDepthStencilFormat = gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000198 presentParameters.BackBufferCount = 1;
daniel@transgaming.com682a37c2012-11-28 19:34:44 +0000199 presentParameters.BackBufferFormat = gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000200 presentParameters.EnableAutoDepthStencil = FALSE;
201 presentParameters.Flags = 0;
202 presentParameters.hDeviceWindow = mWindow;
203 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented
204 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented
205 presentParameters.PresentationInterval = convertInterval(swapInterval);
206 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
207 presentParameters.Windowed = TRUE;
208 presentParameters.BackBufferWidth = backbufferWidth;
209 presentParameters.BackBufferHeight = backbufferHeight;
210
211 // http://crbug.com/140239
212 // http://crbug.com/143434
213 //
214 // Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width
215 // when using the integrated Intel. This rounds the width up rather than down.
216 //
217 // Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. Therefore, when the vendor ID
218 // is not Intel, the back buffer width must be exactly the same width as the window or horizontal scaling will occur.
daniel@transgaming.comd8e36562012-10-31 19:52:19 +0000219 if (mRenderer->getAdapterVendor() == VENDOR_ID_INTEL)
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000220 {
221 presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64;
222 }
223
224 result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
225
226 if (FAILED(result))
227 {
228 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST);
229
230 ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
231 release();
232
shannon.woods@transgaming.comf5f59492013-02-28 23:04:40 +0000233 if (d3d9::isDeviceLostError(result))
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000234 {
235 return EGL_CONTEXT_LOST;
236 }
237 else
238 {
239 return EGL_BAD_ALLOC;
240 }
241 }
242
243 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
244 ASSERT(SUCCEEDED(result));
daniel@transgaming.comb323df02012-11-28 19:41:36 +0000245 InvalidateRect(mWindow, NULL, FALSE);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000246 }
247
daniel@transgaming.coma60160b2012-11-28 19:41:15 +0000248 if (mDepthBufferFormat != GL_NONE)
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000249 {
250 result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight,
daniel@transgaming.com682a37c2012-11-28 19:34:44 +0000251 gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat),
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000252 D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL);
253
254 if (FAILED(result))
255 {
256 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL);
257
258 ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result);
259 release();
260
shannon.woods@transgaming.comf5f59492013-02-28 23:04:40 +0000261 if (d3d9::isDeviceLostError(result))
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000262 {
263 return EGL_CONTEXT_LOST;
264 }
265 else
266 {
267 return EGL_BAD_ALLOC;
268 }
269 }
270 }
271
272 mWidth = backbufferWidth;
273 mHeight = backbufferHeight;
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000274 mSwapInterval = swapInterval;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000275
276 return EGL_SUCCESS;
277}
278
279// parameters should be validated/clamped by caller
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +0000280EGLint SwapChain9::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000281{
282 if (!mSwapChain)
283 {
284 return EGL_SUCCESS;
285 }
286
287 IDirect3DDevice9 *device = mRenderer->getDevice();
288
289 // Disable all pipeline operations
290 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
291 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
292 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
293 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
294 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
295 device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
296 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
297 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
298 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
299 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
300 device->SetPixelShader(NULL);
301 device->SetVertexShader(NULL);
302
303 device->SetRenderTarget(0, mBackBuffer);
304 device->SetDepthStencilSurface(NULL);
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 D3DVIEWPORT9 viewport = {0, 0, mWidth, 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, NULL);
339
340 RECT rect =
341 {
342 x, mHeight - y - height,
343 x + width, mHeight - y
344 };
345
346 HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0);
347
daniel@transgaming.comc43a6052012-11-28 19:41:51 +0000348 mRenderer->markAllStateDirty();
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000349
shannon.woods@transgaming.comf5f59492013-02-28 23:04:40 +0000350 if (d3d9::isDeviceLostError(result))
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000351 {
352 return EGL_CONTEXT_LOST;
353 }
354
355 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
356 {
357 return EGL_BAD_ALLOC;
358 }
359
360 ASSERT(SUCCEEDED(result));
361
362 return EGL_SUCCESS;
363}
364
365// Increments refcount on surface.
366// caller must Release() the returned surface
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +0000367IDirect3DSurface9 *SwapChain9::getRenderTarget()
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000368{
369 if (mRenderTarget)
370 {
371 mRenderTarget->AddRef();
372 }
373
374 return mRenderTarget;
375}
376
377// Increments refcount on surface.
378// caller must Release() the returned surface
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +0000379IDirect3DSurface9 *SwapChain9::getDepthStencil()
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000380{
381 if (mDepthStencil)
382 {
383 mDepthStencil->AddRef();
384 }
385
386 return mDepthStencil;
387}
388
389// Increments refcount on texture.
390// caller must Release() the returned texture
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +0000391IDirect3DTexture9 *SwapChain9::getOffscreenTexture()
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000392{
393 if (mOffscreenTexture)
394 {
395 mOffscreenTexture->AddRef();
396 }
397
398 return mOffscreenTexture;
399}
400
daniel@transgaming.comd733bb82012-11-28 20:53:40 +0000401SwapChain9 *SwapChain9::makeSwapChain9(SwapChain *swapChain)
402{
apatrick@chromium.org8b400b12013-01-30 21:53:40 +0000403 ASSERT(HAS_DYNAMIC_TYPE(rx::SwapChain9*, swapChain));
daniel@transgaming.comd733bb82012-11-28 20:53:40 +0000404 return static_cast<rx::SwapChain9*>(swapChain);
405}
406
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000407}