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 #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 (d3d9::isDeviceLostError(result)) 355 { 356 return EGL_CONTEXT_LOST; 357 } 358 359 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR) 360 { 361 return EGL_BAD_ALLOC; 362 } 363 364 ASSERT(SUCCEEDED(result)); 365 366 return EGL_SUCCESS; 367 } 368 369 // Increments refcount on surface. 370 // caller must Release() the returned surface 371 IDirect3DSurface9 *SwapChain9::getRenderTarget() 372 { 373 if (mRenderTarget) 374 { 375 mRenderTarget->AddRef(); 376 } 377 378 return mRenderTarget; 379 } 380 381 // Increments refcount on surface. 382 // caller must Release() the returned surface 383 IDirect3DSurface9 *SwapChain9::getDepthStencil() 384 { 385 if (mDepthStencil) 386 { 387 mDepthStencil->AddRef(); 388 } 389 390 return mDepthStencil; 391 } 392 393 // Increments refcount on texture. 394 // caller must Release() the returned texture 395 IDirect3DTexture9 *SwapChain9::getOffscreenTexture() 396 { 397 if (mOffscreenTexture) 398 { 399 mOffscreenTexture->AddRef(); 400 } 401 402 return mOffscreenTexture; 403 } 404 405 SwapChain9 *SwapChain9::makeSwapChain9(SwapChain *swapChain) 406 { 407 ASSERT(HAS_DYNAMIC_TYPE(rx::SwapChain9*, swapChain)); 408 return static_cast<rx::SwapChain9*>(swapChain); 409 } 410 411 void SwapChain9::recreate() 412 { 413 if (!mSwapChain) 414 { 415 return; 416 } 417 418 IDirect3DDevice9 *device = mRenderer->getDevice(); 419 if (device == NULL) 420 { 421 return; 422 } 423 424 D3DPRESENT_PARAMETERS presentParameters; 425 HRESULT result = mSwapChain->GetPresentParameters(&presentParameters); 426 ASSERT(SUCCEEDED(result)); 427 428 IDirect3DSwapChain9* newSwapChain = NULL; 429 result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain); 430 if (FAILED(result)) 431 { 432 return; 433 } 434 435 mSwapChain->Release(); 436 mSwapChain = newSwapChain; 437 438 mBackBuffer->Release(); 439 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); 440 ASSERT(SUCCEEDED(result)); 441 } 442 443 } 444