blob: 62e76baf0dcbe354e1abb931c16d60d376c6b887 [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.comd8e36562012-10-31 19:52:19 +000014#include "libGLESv2/renderer/renderer9_utils.h" // D3D9_REPLACE
15#include "libGLESv2/renderer/Renderer9.h" // D3D9_REPLACE
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.comd8e36562012-10-31 19:52:19 +000022SwapChain::SwapChain(Renderer9 *renderer, HWND window, HANDLE shareHandle,
daniel@transgaming.com3c720782012-10-31 18:42:34 +000023 GLenum backBufferFormat, GLenum depthBufferFormat)
24 : mRenderer(renderer), mWindow(window), mShareHandle(shareHandle),
25 mBackBufferFormat(backBufferFormat), mDepthBufferFormat(depthBufferFormat)
26{
27 mSwapChain = NULL;
28 mBackBuffer = NULL;
29 mDepthStencil = NULL;
30 mRenderTarget = NULL;
31 mOffscreenTexture = NULL;
32 mWidth = -1;
33 mHeight = -1;
34}
35
36SwapChain::~SwapChain()
37{
38 release();
39}
40
41void SwapChain::release()
42{
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
92// D3D9_REPLACE
93EGLint SwapChain::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
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000134 HANDLE *pShareHandle = NULL;
135 if (!mWindow && mRenderer->getShareHandleSupport())
136 {
137 pShareHandle = &mShareHandle;
138 }
139
140 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
daniel@transgaming.com682a37c2012-11-28 19:34:44 +0000141 gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat), D3DPOOL_DEFAULT,
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000142 &mOffscreenTexture, pShareHandle);
143 if (FAILED(result))
144 {
145 ERR("Could not create offscreen texture: %08lX", result);
146 release();
147
148 if(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};
daniel@transgaming.com682a37c2012-11-28 19:34:44 +0000192 presentParameters.AutoDepthStencilFormat = gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000193 presentParameters.BackBufferCount = 1;
daniel@transgaming.com682a37c2012-11-28 19:34:44 +0000194 presentParameters.BackBufferFormat = gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000195 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.
daniel@transgaming.comd8e36562012-10-31 19:52:19 +0000214 if (mRenderer->getAdapterVendor() == VENDOR_ID_INTEL)
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000215 {
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(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 }
241
242 if (mDepthBufferFormat != D3DFMT_UNKNOWN)
243 {
244 result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight,
daniel@transgaming.com682a37c2012-11-28 19:34:44 +0000245 gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat),
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000246 D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL);
247
248 if (FAILED(result))
249 {
250 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL);
251
252 ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result);
253 release();
254
255 if(isDeviceLostError(result))
256 {
257 return EGL_CONTEXT_LOST;
258 }
259 else
260 {
261 return EGL_BAD_ALLOC;
262 }
263 }
264 }
265
266 mWidth = backbufferWidth;
267 mHeight = backbufferHeight;
268
269 return EGL_SUCCESS;
270}
271
272// parameters should be validated/clamped by caller
273EGLint SwapChain::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
274{
275 if (!mSwapChain)
276 {
277 return EGL_SUCCESS;
278 }
279
280 IDirect3DDevice9 *device = mRenderer->getDevice();
281
282 // Disable all pipeline operations
283 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
284 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
285 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
286 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
287 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
288 device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
289 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
290 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
291 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
292 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
293 device->SetPixelShader(NULL);
294 device->SetVertexShader(NULL);
295
296 device->SetRenderTarget(0, mBackBuffer);
297 device->SetDepthStencilSurface(NULL);
298
299 device->SetTexture(0, mOffscreenTexture);
300 device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
301 device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
302 device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
303 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
304 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
305 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
306 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
307 device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
308
309 D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f};
310 device->SetViewport(&viewport);
311
312 float x1 = x - 0.5f;
313 float y1 = (mHeight - y - height) - 0.5f;
314 float x2 = (x + width) - 0.5f;
315 float y2 = (mHeight - y) - 0.5f;
316
317 float u1 = x / float(mWidth);
318 float v1 = y / float(mHeight);
319 float u2 = (x + width) / float(mWidth);
320 float v2 = (y + height) / float(mHeight);
321
322 float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2},
323 {x2, y1, 0.0f, 1.0f, u2, v2},
324 {x2, y2, 0.0f, 1.0f, u2, v1},
325 {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v
326
327 mRenderer->startScene();
328 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
329 mRenderer->endScene();
330
331 device->SetTexture(0, NULL);
332
333 RECT rect =
334 {
335 x, mHeight - y - height,
336 x + width, mHeight - y
337 };
338
339 HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0);
340
341 gl::Context *context = static_cast<gl::Context*>(glGetCurrentContext());
342 if (context)
343 {
344 context->markAllStateDirty();
345 }
346
347 if (isDeviceLostError(result))
348 {
349 return EGL_CONTEXT_LOST;
350 }
351
352 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
353 {
354 return EGL_BAD_ALLOC;
355 }
356
357 ASSERT(SUCCEEDED(result));
358
359 return EGL_SUCCESS;
360}
361
362// Increments refcount on surface.
363// caller must Release() the returned surface
364IDirect3DSurface9 *SwapChain::getRenderTarget()
365{
366 if (mRenderTarget)
367 {
368 mRenderTarget->AddRef();
369 }
370
371 return mRenderTarget;
372}
373
374// Increments refcount on surface.
375// caller must Release() the returned surface
376IDirect3DSurface9 *SwapChain::getDepthStencil()
377{
378 if (mDepthStencil)
379 {
380 mDepthStencil->AddRef();
381 }
382
383 return mDepthStencil;
384}
385
386// Increments refcount on texture.
387// caller must Release() the returned texture
388IDirect3DTexture9 *SwapChain::getOffscreenTexture()
389{
390 if (mOffscreenTexture)
391 {
392 mOffscreenTexture->AddRef();
393 }
394
395 return mOffscreenTexture;
396}
397
398}