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 switch(interval) 75 { 76 case 0: return D3DPRESENT_INTERVAL_IMMEDIATE; 77 case 1: return D3DPRESENT_INTERVAL_ONE; 78 case 2: return D3DPRESENT_INTERVAL_TWO; 79 case 3: return D3DPRESENT_INTERVAL_THREE; 80 case 4: return D3DPRESENT_INTERVAL_FOUR; 81 default: UNREACHABLE(); 82 } 83 84 return D3DPRESENT_INTERVAL_DEFAULT; 85 } 86 87 EGLint SwapChain9::resize(int backbufferWidth, int backbufferHeight) 88 { 89 // D3D9 does not support resizing swap chains without recreating them 90 return reset(backbufferWidth, backbufferHeight, mSwapInterval); 91 } 92 93 EGLint SwapChain9::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 134 HANDLE *pShareHandle = NULL; 135 if (!mWindow && mRenderer->getShareHandleSupport()) 136 { 137 pShareHandle = &mShareHandle; 138 } 139 140 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET, 141 gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat), D3DPOOL_DEFAULT, 142 &mOffscreenTexture, pShareHandle); 143 if (FAILED(result)) 144 { 145 ERR("Could not create offscreen texture: %08lX", result); 146 release(); 147 148 if (d3d9::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}; 192 presentParameters.AutoDepthStencilFormat = gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat); 193 presentParameters.BackBufferCount = 1; 194 presentParameters.BackBufferFormat = gl_d3d9::ConvertRenderbufferFormat(mBackBufferFormat); 195 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. 214 if (mRenderer->getAdapterVendor() == VENDOR_ID_INTEL) 215 { 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 (d3d9::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 InvalidateRect(mWindow, NULL, FALSE); 241 } 242 243 if (mDepthBufferFormat != GL_NONE) 244 { 245 result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight, 246 gl_d3d9::ConvertRenderbufferFormat(mDepthBufferFormat), 247 D3DMULTISAMPLE_NONE, 0, FALSE, &mDepthStencil, NULL); 248 249 if (FAILED(result)) 250 { 251 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL); 252 253 ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result); 254 release(); 255 256 if (d3d9::isDeviceLostError(result)) 257 { 258 return EGL_CONTEXT_LOST; 259 } 260 else 261 { 262 return EGL_BAD_ALLOC; 263 } 264 } 265 } 266 267 mWidth = backbufferWidth; 268 mHeight = backbufferHeight; 269 mSwapInterval = swapInterval; 270 271 return EGL_SUCCESS; 272 } 273 274 // parameters should be validated/clamped by caller 275 EGLint SwapChain9::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) 276 { 277 if (!mSwapChain) 278 { 279 return EGL_SUCCESS; 280 } 281 282 IDirect3DDevice9 *device = mRenderer->getDevice(); 283 284 // Disable all pipeline operations 285 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); 286 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); 287 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); 288 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); 289 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); 290 device->SetRenderState(D3DRS_STENCILENABLE, FALSE); 291 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0); 292 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED); 293 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE); 294 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); 295 device->SetPixelShader(NULL); 296 device->SetVertexShader(NULL); 297 298 device->SetRenderTarget(0, mBackBuffer); 299 device->SetDepthStencilSurface(NULL); 300 301 device->SetTexture(0, mOffscreenTexture); 302 device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); 303 device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); 304 device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); 305 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); 306 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); 307 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); 308 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); 309 device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1); 310 311 D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f}; 312 device->SetViewport(&viewport); 313 314 float x1 = x - 0.5f; 315 float y1 = (mHeight - y - height) - 0.5f; 316 float x2 = (x + width) - 0.5f; 317 float y2 = (mHeight - y) - 0.5f; 318 319 float u1 = x / float(mWidth); 320 float v1 = y / float(mHeight); 321 float u2 = (x + width) / float(mWidth); 322 float v2 = (y + height) / float(mHeight); 323 324 float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2}, 325 {x2, y1, 0.0f, 1.0f, u2, v2}, 326 {x2, y2, 0.0f, 1.0f, u2, v1}, 327 {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v 328 329 mRenderer->startScene(); 330 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float)); 331 mRenderer->endScene(); 332 333 device->SetTexture(0, NULL); 334 335 RECT rect = 336 { 337 x, mHeight - y - height, 338 x + width, mHeight - y 339 }; 340 341 HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0); 342 343 mRenderer->markAllStateDirty(); 344 345 if (d3d9::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 362 IDirect3DSurface9 *SwapChain9::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 374 IDirect3DSurface9 *SwapChain9::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 386 IDirect3DTexture9 *SwapChain9::getOffscreenTexture() 387 { 388 if (mOffscreenTexture) 389 { 390 mOffscreenTexture->AddRef(); 391 } 392 393 return mOffscreenTexture; 394 } 395 396 SwapChain9 *SwapChain9::makeSwapChain9(SwapChain *swapChain) 397 { 398 ASSERT(HAS_DYNAMIC_TYPE(rx::SwapChain9*, swapChain)); 399 return static_cast<rx::SwapChain9*>(swapChain); 400 } 401 402 void SwapChain9::recreate() 403 { 404 if (!mSwapChain) 405 { 406 return; 407 } 408 409 IDirect3DDevice9 *device = mRenderer->getDevice(); 410 if (device == NULL) 411 { 412 return; 413 } 414 415 D3DPRESENT_PARAMETERS presentParameters; 416 HRESULT result = mSwapChain->GetPresentParameters(&presentParameters); 417 ASSERT(SUCCEEDED(result)); 418 419 IDirect3DSwapChain9* newSwapChain = NULL; 420 result = device->CreateAdditionalSwapChain(&presentParameters, &newSwapChain); 421 if (FAILED(result)) 422 { 423 return; 424 } 425 426 mSwapChain->Release(); 427 mSwapChain = newSwapChain; 428 429 mBackBuffer->Release(); 430 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); 431 ASSERT(SUCCEEDED(result)); 432 } 433 434 } 435