SwapChain9.cpp (15101B)
1 // 2 // Copyright 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 // SwapChain9.cpp: Implements a back-end specific class for the D3D9 swap chain. 8 9 #include "libANGLE/renderer/d3d/d3d9/SwapChain9.h" 10 11 #include "libANGLE/features.h" 12 #include "libANGLE/renderer/d3d/d3d9/NativeWindow9.h" 13 #include "libANGLE/renderer/d3d/d3d9/Renderer9.h" 14 #include "libANGLE/renderer/d3d/d3d9/formatutils9.h" 15 #include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h" 16 17 namespace rx 18 { 19 20 SwapChain9::SwapChain9(Renderer9 *renderer, 21 NativeWindow9 *nativeWindow, 22 HANDLE shareHandle, 23 IUnknown *d3dTexture, 24 GLenum backBufferFormat, 25 GLenum depthBufferFormat, 26 EGLint orientation) 27 : SwapChainD3D(shareHandle, d3dTexture, backBufferFormat, depthBufferFormat), 28 mRenderer(renderer), 29 mWidth(-1), 30 mHeight(-1), 31 mSwapInterval(-1), 32 mNativeWindow(nativeWindow), 33 mSwapChain(nullptr), 34 mBackBuffer(nullptr), 35 mRenderTarget(nullptr), 36 mDepthStencil(nullptr), 37 mOffscreenTexture(nullptr), 38 mColorRenderTarget(this, false), 39 mDepthStencilRenderTarget(this, true) 40 { 41 ASSERT(orientation == 0); 42 } 43 44 SwapChain9::~SwapChain9() 45 { 46 release(); 47 } 48 49 void SwapChain9::release() 50 { 51 SafeRelease(mSwapChain); 52 SafeRelease(mBackBuffer); 53 SafeRelease(mDepthStencil); 54 SafeRelease(mRenderTarget); 55 SafeRelease(mOffscreenTexture); 56 57 if (mNativeWindow->getNativeWindow()) 58 { 59 mShareHandle = nullptr; 60 } 61 } 62 63 static DWORD convertInterval(EGLint interval) 64 { 65 #if ANGLE_VSYNC == ANGLE_DISABLED 66 return D3DPRESENT_INTERVAL_IMMEDIATE; 67 #else 68 switch (interval) 69 { 70 case 0: 71 return D3DPRESENT_INTERVAL_IMMEDIATE; 72 case 1: 73 return D3DPRESENT_INTERVAL_ONE; 74 case 2: 75 return D3DPRESENT_INTERVAL_TWO; 76 case 3: 77 return D3DPRESENT_INTERVAL_THREE; 78 case 4: 79 return D3DPRESENT_INTERVAL_FOUR; 80 default: 81 UNREACHABLE(); 82 } 83 84 return D3DPRESENT_INTERVAL_DEFAULT; 85 #endif 86 } 87 88 EGLint SwapChain9::resize(DisplayD3D *displayD3D, int backbufferWidth, int backbufferHeight) 89 { 90 // D3D9 does not support resizing swap chains without recreating them 91 return reset(displayD3D, backbufferWidth, backbufferHeight, mSwapInterval); 92 } 93 94 EGLint SwapChain9::reset(DisplayD3D *displayD3D, 95 int backbufferWidth, 96 int backbufferHeight, 97 EGLint swapInterval) 98 { 99 IDirect3DDevice9 *device = mRenderer->getDevice(); 100 101 if (device == nullptr) 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 SafeRelease(mSwapChain); 115 SafeRelease(mBackBuffer); 116 SafeRelease(mOffscreenTexture); 117 SafeRelease(mDepthStencil); 118 119 const d3d9::TextureFormat &backBufferd3dFormatInfo = 120 d3d9::GetTextureFormatInfo(mOffscreenRenderTargetFormat); 121 if (mD3DTexture != nullptr) 122 { 123 result = mD3DTexture->QueryInterface(&mOffscreenTexture); 124 ASSERT(SUCCEEDED(result)); 125 } 126 else 127 { 128 HANDLE *pShareHandle = nullptr; 129 if (!mNativeWindow->getNativeWindow() && mRenderer->getShareHandleSupport()) 130 { 131 pShareHandle = &mShareHandle; 132 } 133 134 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET, 135 backBufferd3dFormatInfo.texFormat, D3DPOOL_DEFAULT, 136 &mOffscreenTexture, pShareHandle); 137 if (FAILED(result)) 138 { 139 ERR() << "Could not create offscreen texture, " << gl::FmtHR(result); 140 release(); 141 142 if (d3d9::isDeviceLostError(result)) 143 { 144 return EGL_CONTEXT_LOST; 145 } 146 else 147 { 148 return EGL_BAD_ALLOC; 149 } 150 } 151 } 152 153 IDirect3DSurface9 *oldRenderTarget = mRenderTarget; 154 155 result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget); 156 ASSERT(SUCCEEDED(result)); 157 158 if (oldRenderTarget) 159 { 160 RECT rect = {0, 0, mWidth, mHeight}; 161 162 if (rect.right > static_cast<LONG>(backbufferWidth)) 163 { 164 rect.right = backbufferWidth; 165 } 166 167 if (rect.bottom > static_cast<LONG>(backbufferHeight)) 168 { 169 rect.bottom = backbufferHeight; 170 } 171 172 mRenderer->endScene(); 173 174 result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE); 175 ASSERT(SUCCEEDED(result)); 176 177 SafeRelease(oldRenderTarget); 178 } 179 180 const d3d9::TextureFormat &depthBufferd3dFormatInfo = 181 d3d9::GetTextureFormatInfo(mDepthBufferFormat); 182 183 // Don't create a swapchain for NULLREF devices 184 D3DDEVTYPE deviceType = mRenderer->getD3D9DeviceType(); 185 EGLNativeWindowType window = mNativeWindow->getNativeWindow(); 186 if (window && deviceType != D3DDEVTYPE_NULLREF) 187 { 188 D3DPRESENT_PARAMETERS presentParameters = {}; 189 presentParameters.AutoDepthStencilFormat = depthBufferd3dFormatInfo.renderFormat; 190 presentParameters.BackBufferCount = 1; 191 presentParameters.BackBufferFormat = backBufferd3dFormatInfo.renderFormat; 192 presentParameters.EnableAutoDepthStencil = FALSE; 193 presentParameters.Flags = 0; 194 presentParameters.hDeviceWindow = window; 195 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented 196 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented 197 presentParameters.PresentationInterval = convertInterval(swapInterval); 198 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD; 199 presentParameters.Windowed = TRUE; 200 presentParameters.BackBufferWidth = backbufferWidth; 201 presentParameters.BackBufferHeight = backbufferHeight; 202 203 // http://crbug.com/140239 204 // http://crbug.com/143434 205 // 206 // Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a 207 // multiple of 64 pixels in width when using the integrated Intel. This rounds the width up 208 // rather than down. 209 // 210 // Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. 211 // Therefore, when the vendor ID is not Intel, the back buffer width must be exactly the 212 // same width as the window or horizontal scaling will occur. 213 if (IsIntel(mRenderer->getVendorId())) 214 { 215 presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64; 216 } 217 218 result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain); 219 220 if (FAILED(result)) 221 { 222 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || 223 result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST); 224 225 ERR() << "Could not create additional swap chains or offscreen surfaces, " 226 << gl::FmtHR(result); 227 release(); 228 229 if (d3d9::isDeviceLostError(result)) 230 { 231 return EGL_CONTEXT_LOST; 232 } 233 else 234 { 235 return EGL_BAD_ALLOC; 236 } 237 } 238 239 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); 240 ASSERT(SUCCEEDED(result)); 241 InvalidateRect(window, nullptr, FALSE); 242 } 243 244 if (mDepthBufferFormat != GL_NONE) 245 { 246 result = device->CreateDepthStencilSurface( 247 backbufferWidth, backbufferHeight, depthBufferd3dFormatInfo.renderFormat, 248 D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, nullptr); 249 250 if (FAILED(result)) 251 { 252 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || 253 result == D3DERR_INVALIDCALL); 254 255 ERR() << "Could not create depthstencil surface for new swap chain, " 256 << gl::FmtHR(result); 257 release(); 258 259 if (d3d9::isDeviceLostError(result)) 260 { 261 return EGL_CONTEXT_LOST; 262 } 263 else 264 { 265 return EGL_BAD_ALLOC; 266 } 267 } 268 } 269 270 mWidth = backbufferWidth; 271 mHeight = backbufferHeight; 272 mSwapInterval = swapInterval; 273 274 return EGL_SUCCESS; 275 } 276 277 // parameters should be validated/clamped by caller 278 EGLint SwapChain9::swapRect(DisplayD3D *displayD3D, EGLint x, EGLint y, EGLint width, EGLint height) 279 { 280 if (!mSwapChain) 281 { 282 return EGL_SUCCESS; 283 } 284 285 IDirect3DDevice9 *device = mRenderer->getDevice(); 286 287 // Disable all pipeline operations 288 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); 289 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); 290 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); 291 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); 292 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); 293 device->SetRenderState(D3DRS_STENCILENABLE, FALSE); 294 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0); 295 device->SetRenderState(D3DRS_COLORWRITEENABLE, 296 D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | 297 D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED); 298 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE); 299 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); 300 device->SetPixelShader(nullptr); 301 device->SetVertexShader(nullptr); 302 303 device->SetRenderTarget(0, mBackBuffer); 304 device->SetDepthStencilSurface(nullptr); 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 for (UINT streamIndex = 0; streamIndex < gl::MAX_VERTEX_ATTRIBS; streamIndex++) 317 { 318 device->SetStreamSourceFreq(streamIndex, 1); 319 } 320 321 D3DVIEWPORT9 viewport = {0, 0, static_cast<DWORD>(mWidth), static_cast<DWORD>(mHeight), 322 0.0f, 1.0f}; 323 device->SetViewport(&viewport); 324 325 float x1 = x - 0.5f; 326 float y1 = (mHeight - y - height) - 0.5f; 327 float x2 = (x + width) - 0.5f; 328 float y2 = (mHeight - y) - 0.5f; 329 330 float u1 = x / float(mWidth); 331 float v1 = y / float(mHeight); 332 float u2 = (x + width) / float(mWidth); 333 float v2 = (y + height) / float(mHeight); 334 335 float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2}, 336 {x2, y1, 0.0f, 1.0f, u2, v2}, 337 {x2, y2, 0.0f, 1.0f, u2, v1}, 338 {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v 339 340 mRenderer->startScene(); 341 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float)); 342 mRenderer->endScene(); 343 344 device->SetTexture(0, nullptr); 345 346 RECT rect = {static_cast<LONG>(x), static_cast<LONG>(mHeight - y - height), 347 static_cast<LONG>(x + width), static_cast<LONG>(mHeight - y)}; 348 349 HRESULT result = mSwapChain->Present(&rect, &rect, nullptr, nullptr, 0); 350 351 mRenderer->markAllStateDirty(); 352 353 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || 354 result == D3DERR_DRIVERINTERNALERROR) 355 { 356 return EGL_BAD_ALLOC; 357 } 358 359 // On Windows 8 systems, IDirect3DSwapChain9::Present sometimes returns 0x88760873 when the 360 // windows is in the process of entering/exiting fullscreen. This code doesn't seem to have any 361 // documentation. The device appears to be ok after emitting this error so simply return a 362 // failure to swap. 363 if (result == static_cast<HRESULT>(0x88760873) || result == static_cast<HRESULT>(0x88760872)) 364 { 365 return EGL_BAD_MATCH; 366 } 367 368 // http://crbug.com/313210 369 // If our swap failed, trigger a device lost event. Resetting will work around an AMD-specific 370 // device removed bug with lost contexts when reinstalling drivers. 371 if (FAILED(result)) 372 { 373 mRenderer->notifyDeviceLost(); 374 return EGL_CONTEXT_LOST; 375 } 376 377 return EGL_SUCCESS; 378 } 379 380 // Increments refcount on surface. 381 // caller must Release() the returned surface 382 // TODO: remove the AddRef to match SwapChain11 383 IDirect3DSurface9 *SwapChain9::getRenderTarget() 384 { 385 if (mRenderTarget) 386 { 387 mRenderTarget->AddRef(); 388 } 389 390 return mRenderTarget; 391 } 392 393 // Increments refcount on surface. 394 // caller must Release() the returned surface 395 // TODO: remove the AddRef to match SwapChain11 396 IDirect3DSurface9 *SwapChain9::getDepthStencil() 397 { 398 if (mDepthStencil) 399 { 400 mDepthStencil->AddRef(); 401 } 402 403 return mDepthStencil; 404 } 405 406 // Increments refcount on texture. 407 // caller must Release() the returned texture 408 // TODO: remove the AddRef to match SwapChain11 409 IDirect3DTexture9 *SwapChain9::getOffscreenTexture() 410 { 411 if (mOffscreenTexture) 412 { 413 mOffscreenTexture->AddRef(); 414 } 415 416 return mOffscreenTexture; 417 } 418 419 void *SwapChain9::getKeyedMutex() 420 { 421 UNREACHABLE(); 422 return nullptr; 423 } 424 425 egl::Error SwapChain9::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) 426 { 427 UNREACHABLE(); 428 return egl::EglBadSurface(); 429 } 430 431 void SwapChain9::recreate() 432 { 433 if (!mSwapChain) 434 { 435 return; 436 } 437 438 IDirect3DDevice9 *device = mRenderer->getDevice(); 439 if (device == nullptr) 440 { 441 return; 442 } 443 444 D3DPRESENT_PARAMETERS presentParameters; 445 HRESULT result = mSwapChain->GetPresentParameters(&presentParameters); 446 ASSERT(SUCCEEDED(result)); 447 448 IDirect3DSwapChain9 *newSwapChain = nullptr; 449 result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain); 450 if (FAILED(result)) 451 { 452 return; 453 } 454 455 SafeRelease(mSwapChain); 456 mSwapChain = newSwapChain; 457 458 SafeRelease(mBackBuffer); 459 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); 460 ASSERT(SUCCEEDED(result)); 461 } 462 463 RenderTargetD3D *SwapChain9::getColorRenderTarget() 464 { 465 return &mColorRenderTarget; 466 } 467 468 RenderTargetD3D *SwapChain9::getDepthStencilRenderTarget() 469 { 470 return &mDepthStencilRenderTarget; 471 } 472 } // namespace rx