blob: 91c669b8f1a125211b4bba8b5a598c97e678984d [file] [log] [blame]
daniel@transgaming.com3c720782012-10-31 18:42:34 +00001//
2// Copyright (c) 2012 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// SwapChain.cpp: Implements a back-end specific class that hides the details of the
8// implementation-specific swapchain.
9
10#include "libGLESv2/renderer/SwapChain.h"
11
12#include "common/debug.h"
13#include "libGLESv2/utilities.h"
daniel@transgaming.com2507f412012-10-31 18:46:48 +000014#include "libGLESv2/renderer/Renderer9.h" // D3D9_REPLACE
daniel@transgaming.com3c720782012-10-31 18:42:34 +000015#include "libGLESv2/Context.h"
16
17namespace renderer
18{
19
daniel@transgaming.com2507f412012-10-31 18:46:48 +000020SwapChain::SwapChain(Renderer9 *renderer, HWND window, HANDLE shareHandle,
daniel@transgaming.com3c720782012-10-31 18:42:34 +000021 GLenum backBufferFormat, GLenum depthBufferFormat)
22 : mRenderer(renderer), mWindow(window), mShareHandle(shareHandle),
23 mBackBufferFormat(backBufferFormat), mDepthBufferFormat(depthBufferFormat)
24{
25 mSwapChain = NULL;
26 mBackBuffer = NULL;
27 mDepthStencil = NULL;
28 mRenderTarget = NULL;
29 mOffscreenTexture = NULL;
30 mWidth = -1;
31 mHeight = -1;
32}
33
34SwapChain::~SwapChain()
35{
36 release();
37}
38
39void SwapChain::release()
40{
41 if (mSwapChain)
42 {
43 mSwapChain->Release();
44 mSwapChain = NULL;
45 }
46
47 if (mBackBuffer)
48 {
49 mBackBuffer->Release();
50 mBackBuffer = NULL;
51 }
52
53 if (mDepthStencil)
54 {
55 mDepthStencil->Release();
56 mDepthStencil = NULL;
57 }
58
59 if (mRenderTarget)
60 {
61 mRenderTarget->Release();
62 mRenderTarget = NULL;
63 }
64
65 if (mOffscreenTexture)
66 {
67 mOffscreenTexture->Release();
68 mOffscreenTexture = NULL;
69 }
70
daniel@transgaming.com21cfaef2012-10-31 18:42:43 +000071 if (mWindow)
72 mShareHandle = NULL;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000073}
74
75static DWORD convertInterval(EGLint interval)
76{
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}
89
90// D3D9_REPLACE
91EGLint SwapChain::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval)
92{
93 IDirect3DDevice9 *device = mRenderer->getDevice();
94
95 if (device == NULL)
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 if (mSwapChain)
109 {
110 mSwapChain->Release();
111 mSwapChain = NULL;
112 }
113
114 if (mBackBuffer)
115 {
116 mBackBuffer->Release();
117 mBackBuffer = NULL;
118 }
119
120 if (mOffscreenTexture)
121 {
122 mOffscreenTexture->Release();
123 mOffscreenTexture = NULL;
124 }
125
126 if (mDepthStencil)
127 {
128 mDepthStencil->Release();
129 mDepthStencil = NULL;
130 }
131
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000132 HANDLE *pShareHandle = NULL;
133 if (!mWindow && mRenderer->getShareHandleSupport())
134 {
135 pShareHandle = &mShareHandle;
136 }
137
138 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
139 es2dx::ConvertRenderbufferFormat(mBackBufferFormat), D3DPOOL_DEFAULT,
140 &mOffscreenTexture, pShareHandle);
141 if (FAILED(result))
142 {
143 ERR("Could not create offscreen texture: %08lX", result);
144 release();
145
146 if(isDeviceLostError(result))
147 {
148 return EGL_CONTEXT_LOST;
149 }
150 else
151 {
152 return EGL_BAD_ALLOC;
153 }
154 }
155
156 IDirect3DSurface9 *oldRenderTarget = mRenderTarget;
157
158 result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
159 ASSERT(SUCCEEDED(result));
160
161 if (oldRenderTarget)
162 {
163 RECT rect =
164 {
165 0, 0,
166 mWidth, mHeight
167 };
168
169 if (rect.right > static_cast<LONG>(backbufferWidth))
170 {
171 rect.right = backbufferWidth;
172 }
173
174 if (rect.bottom > static_cast<LONG>(backbufferHeight))
175 {
176 rect.bottom = backbufferHeight;
177 }
178
179 mRenderer->endScene();
180
181 result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE);
182 ASSERT(SUCCEEDED(result));
183
184 oldRenderTarget->Release();
185 }
186
187 if (mWindow)
188 {
189 D3DPRESENT_PARAMETERS presentParameters = {0};
190 presentParameters.AutoDepthStencilFormat = es2dx::ConvertRenderbufferFormat(mDepthBufferFormat);
191 presentParameters.BackBufferCount = 1;
192 presentParameters.BackBufferFormat = es2dx::ConvertRenderbufferFormat(mBackBufferFormat);
193 presentParameters.EnableAutoDepthStencil = FALSE;
194 presentParameters.Flags = 0;
195 presentParameters.hDeviceWindow = mWindow;
196 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented
197 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented
198 presentParameters.PresentationInterval = convertInterval(swapInterval);
199 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
200 presentParameters.Windowed = TRUE;
201 presentParameters.BackBufferWidth = backbufferWidth;
202 presentParameters.BackBufferHeight = backbufferHeight;
203
204 // http://crbug.com/140239
205 // http://crbug.com/143434
206 //
207 // Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width
208 // when using the integrated Intel. This rounds the width up rather than down.
209 //
210 // Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. Therefore, when the vendor ID
211 // is not Intel, the back buffer width must be exactly the same width as the window or horizontal scaling will occur.
daniel@transgaming.com4ca789e2012-10-31 18:46:40 +0000212 if (mRenderer->getAdapterVendor() == VENDOR_ID_INTEL)
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000213 {
214 presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64;
215 }
216
217 result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
218
219 if (FAILED(result))
220 {
221 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST);
222
223 ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
224 release();
225
226 if(isDeviceLostError(result))
227 {
228 return EGL_CONTEXT_LOST;
229 }
230 else
231 {
232 return EGL_BAD_ALLOC;
233 }
234 }
235
236 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
237 ASSERT(SUCCEEDED(result));
238 }
239
240 if (mDepthBufferFormat != D3DFMT_UNKNOWN)
241 {
242 result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight,
243 es2dx::ConvertRenderbufferFormat(mDepthBufferFormat),
244 D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL);
245
246 if (FAILED(result))
247 {
248 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL);
249
250 ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result);
251 release();
252
253 if(isDeviceLostError(result))
254 {
255 return EGL_CONTEXT_LOST;
256 }
257 else
258 {
259 return EGL_BAD_ALLOC;
260 }
261 }
262 }
263
264 mWidth = backbufferWidth;
265 mHeight = backbufferHeight;
266
267 return EGL_SUCCESS;
268}
269
270// parameters should be validated/clamped by caller
271EGLint SwapChain::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
272{
273 if (!mSwapChain)
274 {
275 return EGL_SUCCESS;
276 }
277
278 IDirect3DDevice9 *device = mRenderer->getDevice();
279
280 // Disable all pipeline operations
281 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
282 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
283 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
284 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
285 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
286 device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
287 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
288 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
289 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
290 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
291 device->SetPixelShader(NULL);
292 device->SetVertexShader(NULL);
293
294 device->SetRenderTarget(0, mBackBuffer);
295 device->SetDepthStencilSurface(NULL);
296
297 device->SetTexture(0, mOffscreenTexture);
298 device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
299 device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
300 device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
301 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
302 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
303 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
304 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
305 device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
306
307 D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f};
308 device->SetViewport(&viewport);
309
310 float x1 = x - 0.5f;
311 float y1 = (mHeight - y - height) - 0.5f;
312 float x2 = (x + width) - 0.5f;
313 float y2 = (mHeight - y) - 0.5f;
314
315 float u1 = x / float(mWidth);
316 float v1 = y / float(mHeight);
317 float u2 = (x + width) / float(mWidth);
318 float v2 = (y + height) / float(mHeight);
319
320 float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2},
321 {x2, y1, 0.0f, 1.0f, u2, v2},
322 {x2, y2, 0.0f, 1.0f, u2, v1},
323 {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v
324
325 mRenderer->startScene();
326 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
327 mRenderer->endScene();
328
329 device->SetTexture(0, NULL);
330
331 RECT rect =
332 {
333 x, mHeight - y - height,
334 x + width, mHeight - y
335 };
336
337 HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0);
338
339 gl::Context *context = static_cast<gl::Context*>(glGetCurrentContext());
340 if (context)
341 {
342 context->markAllStateDirty();
343 }
344
345 if (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
362IDirect3DSurface9 *SwapChain::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
374IDirect3DSurface9 *SwapChain::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
386IDirect3DTexture9 *SwapChain::getOffscreenTexture()
387{
388 if (mOffscreenTexture)
389 {
390 mOffscreenTexture->AddRef();
391 }
392
393 return mOffscreenTexture;
394}
395
396}