1 // 2 // Copyright (c) 2012-2014 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 // SwapChain9.cpp: Implements a back-end specific class for the D3D9 swap chain. 8 9 #include "libGLESv2/renderer/d3d/d3d9/SwapChain9.h" 10 #include "libGLESv2/renderer/d3d/d3d9/renderer9_utils.h" 11 #include "libGLESv2/renderer/d3d/d3d9/formatutils9.h" 12 #include "libGLESv2/renderer/d3d/d3d9/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 SafeRelease(mSwapChain); 39 SafeRelease(mBackBuffer); 40 SafeRelease(mDepthStencil); 41 SafeRelease(mRenderTarget); 42 SafeRelease(mOffscreenTexture); 43 44 if (mWindow) 45 { 46 mShareHandle = NULL; 47 } 48 } 49 50 static DWORD convertInterval(EGLint interval) 51 { 52 #ifdef ANGLE_FORCE_VSYNC_OFF 53 return D3DPRESENT_INTERVAL_IMMEDIATE; 54 #else 55 switch(interval) 56 { 57 case 0: return D3DPRESENT_INTERVAL_IMMEDIATE; 58 case 1: return D3DPRESENT_INTERVAL_ONE; 59 case 2: return D3DPRESENT_INTERVAL_TWO; 60 case 3: return D3DPRESENT_INTERVAL_THREE; 61 case 4: return D3DPRESENT_INTERVAL_FOUR; 62 default: UNREACHABLE(); 63 } 64 65 return D3DPRESENT_INTERVAL_DEFAULT; 66 #endif 67 } 68 69 EGLint SwapChain9::resize(int backbufferWidth, int backbufferHeight) 70 { 71 // D3D9 does not support resizing swap chains without recreating them 72 return reset(backbufferWidth, backbufferHeight, mSwapInterval); 73 } 74 75 EGLint SwapChain9::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval) 76 { 77 IDirect3DDevice9 *device = mRenderer->getDevice(); 78 79 if (device == NULL) 80 { 81 return EGL_BAD_ACCESS; 82 } 83 84 // Evict all non-render target textures to system memory and release all resources 85 // before reallocating them to free up as much video memory as possible. 86 device->EvictManagedResources(); 87 88 HRESULT result; 89 90 // Release specific resources to free up memory for the new render target, while the 91 // old render target still exists for the purpose of preserving its contents. 92 SafeRelease(mSwapChain); 93 SafeRelease(mBackBuffer); 94 SafeRelease(mOffscreenTexture); 95 SafeRelease(mDepthStencil); 96 97 HANDLE *pShareHandle = NULL; 98 if (!mWindow && mRenderer->getShareHandleSupport()) 99 { 100 pShareHandle = &mShareHandle; 101 } 102 103 const d3d9::TextureFormat &backBufferd3dFormatInfo = d3d9::GetTextureFormatInfo(mBackBufferFormat); 104 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET, 105 backBufferd3dFormatInfo.texFormat, D3DPOOL_DEFAULT, &mOffscreenTexture, 106 pShareHandle); 107 if (FAILED(result)) 108 { 109 ERR("Could not create offscreen texture: %08lX", result); 110 release(); 111 112 if (d3d9::isDeviceLostError(result)) 113 { 114 return EGL_CONTEXT_LOST; 115 } 116 else 117 { 118 return EGL_BAD_ALLOC; 119 } 120 } 121 122 IDirect3DSurface9 *oldRenderTarget = mRenderTarget; 123 124 result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget); 125 ASSERT(SUCCEEDED(result)); 126 127 if (oldRenderTarget) 128 { 129 RECT rect = 130 { 131 0, 0, 132 mWidth, mHeight 133 }; 134 135 if (rect.right > static_cast<LONG>(backbufferWidth)) 136 { 137 rect.right = backbufferWidth; 138 } 139 140 if (rect.bottom > static_cast<LONG>(backbufferHeight)) 141 { 142 rect.bottom = backbufferHeight; 143 } 144 145 mRenderer->endScene(); 146 147 result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE); 148 ASSERT(SUCCEEDED(result)); 149 150 SafeRelease(oldRenderTarget); 151 } 152 153 const d3d9::TextureFormat &depthBufferd3dFormatInfo = d3d9::GetTextureFormatInfo(mDepthBufferFormat); 154 155 if (mWindow) 156 { 157 D3DPRESENT_PARAMETERS presentParameters = {0}; 158 presentParameters.AutoDepthStencilFormat = depthBufferd3dFormatInfo.renderFormat; 159 presentParameters.BackBufferCount = 1; 160 presentParameters.BackBufferFormat = backBufferd3dFormatInfo.renderFormat; 161 presentParameters.EnableAutoDepthStencil = FALSE; 162 presentParameters.Flags = 0; 163 presentParameters.hDeviceWindow = mWindow; 164 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented 165 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented 166 presentParameters.PresentationInterval = convertInterval(swapInterval); 167 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD; 168 presentParameters.Windowed = TRUE; 169 presentParameters.BackBufferWidth = backbufferWidth; 170 presentParameters.BackBufferHeight = backbufferHeight; 171 172 // http://crbug.com/140239 173 // http://crbug.com/143434 174 // 175 // Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width 176 // when using the integrated Intel. This rounds the width up rather than down. 177 // 178 // Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. Therefore, when the vendor ID 179 // is not Intel, the back buffer width must be exactly the same width as the window or horizontal scaling will occur. 180 if (mRenderer->getAdapterVendor() == VENDOR_ID_INTEL) 181 { 182 presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64; 183 } 184 185 result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain); 186 187 if (FAILED(result)) 188 { 189 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST); 190 191 ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result); 192 release(); 193 194 if (d3d9::isDeviceLostError(result)) 195 { 196 return EGL_CONTEXT_LOST; 197 } 198 else 199 { 200 return EGL_BAD_ALLOC; 201 } 202 } 203 204 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); 205 ASSERT(SUCCEEDED(result)); 206 InvalidateRect(mWindow, NULL, FALSE); 207 } 208 209 if (mDepthBufferFormat != GL_NONE) 210 { 211 result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight, 212 depthBufferd3dFormatInfo.renderFormat, 213 D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL); 214 215 if (FAILED(result)) 216 { 217 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL); 218 219 ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result); 220 release(); 221 222 if (d3d9::isDeviceLostError(result)) 223 { 224 return EGL_CONTEXT_LOST; 225 } 226 else 227 { 228 return EGL_BAD_ALLOC; 229 } 230 } 231 } 232 233 mWidth = backbufferWidth; 234 mHeight = backbufferHeight; 235 mSwapInterval = swapInterval; 236 237 return EGL_SUCCESS; 238 } 239 240 // parameters should be validated/clamped by caller 241 EGLint SwapChain9::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) 242 { 243 if (!mSwapChain) 244 { 245 return EGL_SUCCESS; 246 } 247 248 IDirect3DDevice9 *device = mRenderer->getDevice(); 249 250 // Disable all pipeline operations 251 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); 252 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); 253 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); 254 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); 255 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); 256 device->SetRenderState(D3DRS_STENCILENABLE, FALSE); 257 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0); 258 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED); 259 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE); 260 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); 261 device->SetPixelShader(NULL); 262 device->SetVertexShader(NULL); 263 264 device->SetRenderTarget(0, mBackBuffer); 265 device->SetDepthStencilSurface(NULL); 266 267 device->SetTexture(0, mOffscreenTexture); 268 device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); 269 device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); 270 device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); 271 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); 272 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); 273 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); 274 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); 275 device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1); 276 277 for (UINT streamIndex = 0; streamIndex < gl::MAX_VERTEX_ATTRIBS; streamIndex++) 278 { 279 device->SetStreamSourceFreq(streamIndex, 1); 280 } 281 282 D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f}; 283 device->SetViewport(&viewport); 284 285 float x1 = x - 0.5f; 286 float y1 = (mHeight - y - height) - 0.5f; 287 float x2 = (x + width) - 0.5f; 288 float y2 = (mHeight - y) - 0.5f; 289 290 float u1 = x / float(mWidth); 291 float v1 = y / float(mHeight); 292 float u2 = (x + width) / float(mWidth); 293 float v2 = (y + height) / float(mHeight); 294 295 float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2}, 296 {x2, y1, 0.0f, 1.0f, u2, v2}, 297 {x2, y2, 0.0f, 1.0f, u2, v1}, 298 {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v 299 300 mRenderer->startScene(); 301 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float)); 302 mRenderer->endScene(); 303 304 device->SetTexture(0, NULL); 305 306 RECT rect = 307 { 308 x, mHeight - y - height, 309 x + width, mHeight - y 310 }; 311 312 HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0); 313 314 mRenderer->markAllStateDirty(); 315 316 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR) 317 { 318 return EGL_BAD_ALLOC; 319 } 320 321 // On Windows 8 systems, IDirect3DSwapChain9::Present sometimes returns 0x88760873 when the windows is 322 // in the process of entering/exiting fullscreen. This code doesn't seem to have any documentation. The 323 // device appears to be ok after emitting this error so simply return a failure to swap. 324 if (result == 0x88760873) 325 { 326 return EGL_BAD_MATCH; 327 } 328 329 // http://crbug.com/313210 330 // If our swap failed, trigger a device lost event. Resetting will work around an AMD-specific 331 // device removed bug with lost contexts when reinstalling drivers. 332 if (FAILED(result)) 333 { 334 mRenderer->notifyDeviceLost(); 335 return EGL_CONTEXT_LOST; 336 } 337 338 return EGL_SUCCESS; 339 } 340 341 // Increments refcount on surface. 342 // caller must Release() the returned surface 343 // TODO: remove the AddRef to match SwapChain11 344 IDirect3DSurface9 *SwapChain9::getRenderTarget() 345 { 346 if (mRenderTarget) 347 { 348 mRenderTarget->AddRef(); 349 } 350 351 return mRenderTarget; 352 } 353 354 // Increments refcount on surface. 355 // caller must Release() the returned surface 356 // TODO: remove the AddRef to match SwapChain11 357 IDirect3DSurface9 *SwapChain9::getDepthStencil() 358 { 359 if (mDepthStencil) 360 { 361 mDepthStencil->AddRef(); 362 } 363 364 return mDepthStencil; 365 } 366 367 // Increments refcount on texture. 368 // caller must Release() the returned texture 369 // TODO: remove the AddRef to match SwapChain11 370 IDirect3DTexture9 *SwapChain9::getOffscreenTexture() 371 { 372 if (mOffscreenTexture) 373 { 374 mOffscreenTexture->AddRef(); 375 } 376 377 return mOffscreenTexture; 378 } 379 380 SwapChain9 *SwapChain9::makeSwapChain9(SwapChain *swapChain) 381 { 382 ASSERT(HAS_DYNAMIC_TYPE(rx::SwapChain9*, swapChain)); 383 return static_cast<rx::SwapChain9*>(swapChain); 384 } 385 386 void SwapChain9::recreate() 387 { 388 if (!mSwapChain) 389 { 390 return; 391 } 392 393 IDirect3DDevice9 *device = mRenderer->getDevice(); 394 if (device == NULL) 395 { 396 return; 397 } 398 399 D3DPRESENT_PARAMETERS presentParameters; 400 HRESULT result = mSwapChain->GetPresentParameters(&presentParameters); 401 ASSERT(SUCCEEDED(result)); 402 403 IDirect3DSwapChain9* newSwapChain = NULL; 404 result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain); 405 if (FAILED(result)) 406 { 407 return; 408 } 409 410 SafeRelease(mSwapChain); 411 mSwapChain = newSwapChain; 412 413 SafeRelease(mBackBuffer); 414 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); 415 ASSERT(SUCCEEDED(result)); 416 } 417 418 } 419