1 // 2 // Copyright (c) 2002-2010 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 // Surface.cpp: Implements the egl::Surface class, representing a drawing surface 8 // such as the client area of a window, including any back buffers. 9 // Implements EGLSurface and related functionality. [EGL 1.4] section 2.2 page 3. 10 11 #include <tchar.h> 12 13 #include "libEGL/Surface.h" 14 15 #include "common/debug.h" 16 17 #include "libEGL/main.h" 18 #include "libEGL/Display.h" 19 20 namespace egl 21 { 22 Surface::Surface(Display *display, const Config *config, HWND window) 23 : mDisplay(display), mConfig(config), mWindow(window) 24 { 25 mSwapChain = NULL; 26 mDepthStencil = NULL; 27 mBackBuffer = NULL; 28 mFlipTexture = NULL; 29 mFlipState = NULL; 30 mPreFlipState = NULL; 31 32 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio 33 mRenderBuffer = EGL_BACK_BUFFER; 34 mSwapBehavior = EGL_BUFFER_PRESERVED; 35 mSwapInterval = -1; 36 setSwapInterval(1); 37 38 subclassWindow(); 39 resetSwapChain(); 40 } 41 42 Surface::~Surface() 43 { 44 unsubclassWindow(); 45 release(); 46 } 47 48 void Surface::release() 49 { 50 if (mSwapChain) 51 { 52 mSwapChain->Release(); 53 mSwapChain = NULL; 54 } 55 56 if (mBackBuffer) 57 { 58 mBackBuffer->Release(); 59 mBackBuffer = NULL; 60 } 61 62 if (mDepthStencil) 63 { 64 mDepthStencil->Release(); 65 mDepthStencil = NULL; 66 } 67 68 if (mFlipTexture) 69 { 70 mFlipTexture->Release(); 71 mFlipTexture = NULL; 72 } 73 74 if (mFlipState) 75 { 76 mFlipState->Release(); 77 mFlipState = NULL; 78 } 79 80 if (mPreFlipState) 81 { 82 mPreFlipState->Release(); 83 mPreFlipState = NULL; 84 } 85 } 86 87 void Surface::resetSwapChain() 88 { 89 RECT windowRect; 90 if (!GetClientRect(getWindowHandle(), &windowRect)) 91 { 92 ASSERT(false); 93 94 ERR("Could not retrieve the window dimensions"); 95 return; 96 } 97 98 resetSwapChain(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top); 99 } 100 101 void Surface::resetSwapChain(int backbufferWidth, int backbufferHeight) 102 { 103 IDirect3DDevice9 *device = mDisplay->getDevice(); 104 105 if (device == NULL) 106 { 107 return; 108 } 109 110 // Evict all non-render target textures to system memory and release all resources 111 // before reallocating them to free up as much video memory as possible. 112 device->EvictManagedResources(); 113 release(); 114 115 D3DPRESENT_PARAMETERS presentParameters = {0}; 116 117 presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat; 118 presentParameters.BackBufferCount = 1; 119 presentParameters.BackBufferFormat = mConfig->mRenderTargetFormat; 120 presentParameters.EnableAutoDepthStencil = FALSE; 121 presentParameters.Flags = 0; 122 presentParameters.hDeviceWindow = getWindowHandle(); 123 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented 124 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented 125 presentParameters.PresentationInterval = mPresentInterval; 126 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD; 127 presentParameters.Windowed = TRUE; 128 presentParameters.BackBufferWidth = backbufferWidth; 129 presentParameters.BackBufferHeight = backbufferHeight; 130 131 HRESULT result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain); 132 133 if (FAILED(result)) 134 { 135 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); 136 137 ERR("Could not create additional swap chains: %08lX", result); 138 release(); 139 return error(EGL_BAD_ALLOC); 140 } 141 142 result = device->CreateDepthStencilSurface(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 143 presentParameters.AutoDepthStencilFormat, presentParameters.MultiSampleType, 144 presentParameters.MultiSampleQuality, FALSE, &mDepthStencil, NULL); 145 146 if (FAILED(result)) 147 { 148 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); 149 150 ERR("Could not create depthstencil surface for new swap chain: %08lX", result); 151 release(); 152 return error(EGL_BAD_ALLOC); 153 } 154 155 ASSERT(SUCCEEDED(result)); 156 157 result = device->CreateTexture(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 1, D3DUSAGE_RENDERTARGET, 158 presentParameters.BackBufferFormat, D3DPOOL_DEFAULT, &mFlipTexture, NULL); 159 160 if (FAILED(result)) 161 { 162 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); 163 164 ERR("Could not create flip texture for new swap chain: %08lX", result); 165 release(); 166 return error(EGL_BAD_ALLOC); 167 } 168 169 mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer); 170 mWidth = presentParameters.BackBufferWidth; 171 mHeight = presentParameters.BackBufferHeight; 172 173 mPresentIntervalDirty = false; 174 175 InvalidateRect(mWindow, NULL, FALSE); 176 177 // The flip state block recorded mFlipTexture so it is now invalid. 178 releaseRecordedState(device); 179 } 180 181 HWND Surface::getWindowHandle() 182 { 183 return mWindow; 184 } 185 186 void Surface::writeRecordableFlipState(IDirect3DDevice9 *device) 187 { 188 // Disable all pipeline operations 189 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); 190 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); 191 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); 192 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); 193 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); 194 device->SetRenderState(D3DRS_STENCILENABLE, FALSE); 195 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0); 196 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED); 197 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE); 198 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); 199 device->SetPixelShader(NULL); 200 device->SetVertexShader(NULL); 201 202 // Just sample the texture 203 device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); 204 device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); 205 device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); 206 device->SetTexture(0, NULL); // The actual texture will change after resizing. But the pre-flip state block must save/restore the texture. 207 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); 208 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); 209 device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE); 210 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); 211 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); 212 device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1); 213 214 RECT scissorRect = {0}; // Scissoring is disabled for flipping, but we need this to capture and restore the old rectangle 215 device->SetScissorRect(&scissorRect); 216 D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f}; 217 device->SetViewport(&viewport); 218 } 219 220 void Surface::applyFlipState(IDirect3DDevice9 *device) 221 { 222 HRESULT hr; 223 224 if (mFlipState == NULL) 225 { 226 // Create two state blocks both recording the states that are changed when swapping. 227 228 // mPreFlipState will record the original state each entry. 229 hr = device->BeginStateBlock(); 230 ASSERT(SUCCEEDED(hr)); 231 writeRecordableFlipState(device); 232 hr = device->EndStateBlock(&mPreFlipState); 233 ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY); 234 235 if (SUCCEEDED(hr)) 236 { 237 mPreFlipState->Capture(); 238 } 239 240 // mFlipState will record the state for the swap operation. 241 hr = device->BeginStateBlock(); 242 ASSERT(SUCCEEDED(hr)); 243 244 writeRecordableFlipState(device); 245 246 hr = device->EndStateBlock(&mFlipState); 247 ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY); 248 249 if (FAILED(hr)) 250 { 251 mFlipState = NULL; 252 mPreFlipState->Release(); 253 mPreFlipState = NULL; 254 } 255 else 256 { 257 hr = mFlipState->Apply(); 258 ASSERT(SUCCEEDED(hr)); 259 } 260 } 261 else 262 { 263 hr = mPreFlipState->Capture(); 264 ASSERT(SUCCEEDED(hr)); 265 hr = mFlipState->Apply(); 266 ASSERT(SUCCEEDED(hr)); 267 } 268 269 device->GetRenderTarget(0, &mPreFlipBackBuffer); 270 device->GetDepthStencilSurface(&mPreFlipDepthStencil); 271 272 device->SetRenderTarget(0, mBackBuffer); 273 device->SetDepthStencilSurface(NULL); 274 } 275 276 void Surface::restoreState(IDirect3DDevice9 *device) 277 { 278 device->SetRenderTarget(0, mPreFlipBackBuffer); 279 device->SetDepthStencilSurface(mPreFlipDepthStencil); 280 281 if (mPreFlipBackBuffer) 282 { 283 mPreFlipBackBuffer->Release(); 284 mPreFlipBackBuffer = NULL; 285 } 286 287 if (mPreFlipDepthStencil) 288 { 289 mPreFlipDepthStencil->Release(); 290 mPreFlipDepthStencil = NULL; 291 } 292 293 mPreFlipState->Apply(); 294 } 295 296 // On the next flip, this will cause the state to be recorded from scratch. 297 // In particular we need to do this if the flip texture changes. 298 void Surface::releaseRecordedState(IDirect3DDevice9 *device) 299 { 300 if (mFlipState) 301 { 302 mFlipState->Release(); 303 mFlipState = NULL; 304 } 305 306 if (mPreFlipState) 307 { 308 mPreFlipState->Release(); 309 mPreFlipState = NULL; 310 } 311 } 312 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner") 313 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc") 314 315 static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { 316 if (message == WM_SIZE) { 317 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty)); 318 if(surf) { 319 surf->checkForOutOfDateSwapChain(); 320 } 321 } 322 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc)); 323 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam); 324 } 325 326 void Surface::subclassWindow() 327 { 328 SetLastError(0); 329 LONG oldWndProc = SetWindowLong(mWindow, GWL_WNDPROC, reinterpret_cast<LONG>(SurfaceWindowProc)); 330 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS) { 331 mWindowSubclassed = false; 332 return; 333 } 334 335 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this)); 336 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc)); 337 mWindowSubclassed = true; 338 } 339 340 void Surface::unsubclassWindow() 341 { 342 if(!mWindowSubclassed) 343 return; 344 345 // un-subclass 346 LONG parentWndFunc = reinterpret_cast<LONG>(GetProp(mWindow, kParentWndProc)); 347 348 // Check the windowproc is still SurfaceWindowProc. 349 // If this assert fails, then it is likely the application has subclassed the 350 // hwnd as well and did not unsubclass before destroying its EGL context. The 351 // application should be modified to either subclass before initializing the 352 // EGL context, or to unsubclass before destroying the EGL context. 353 if(parentWndFunc) { 354 LONG prevWndFunc = SetWindowLong(mWindow, GWL_WNDPROC, parentWndFunc); 355 ASSERT(prevWndFunc == reinterpret_cast<LONG>(SurfaceWindowProc)); 356 } 357 358 RemoveProp(mWindow, kSurfaceProperty); 359 RemoveProp(mWindow, kParentWndProc); 360 mWindowSubclassed = false; 361 } 362 363 bool Surface::checkForOutOfDateSwapChain() 364 { 365 RECT client; 366 if (!GetClientRect(getWindowHandle(), &client)) 367 { 368 ASSERT(false); 369 return false; 370 } 371 372 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information. 373 int clientWidth = client.right - client.left; 374 int clientHeight = client.bottom - client.top; 375 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight(); 376 377 if (sizeDirty || mPresentIntervalDirty) 378 { 379 resetSwapChain(clientWidth, clientHeight); 380 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this) 381 { 382 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this); 383 } 384 385 return true; 386 } 387 return false; 388 } 389 390 DWORD Surface::convertInterval(EGLint interval) 391 { 392 switch(interval) 393 { 394 case 0: return D3DPRESENT_INTERVAL_IMMEDIATE; 395 case 1: return D3DPRESENT_INTERVAL_ONE; 396 case 2: return D3DPRESENT_INTERVAL_TWO; 397 case 3: return D3DPRESENT_INTERVAL_THREE; 398 case 4: return D3DPRESENT_INTERVAL_FOUR; 399 default: UNREACHABLE(); 400 } 401 402 return D3DPRESENT_INTERVAL_DEFAULT; 403 } 404 405 406 bool Surface::swap() 407 { 408 if (mSwapChain) 409 { 410 IDirect3DDevice9 *device = mDisplay->getDevice(); 411 412 applyFlipState(device); 413 device->SetTexture(0, mFlipTexture); 414 415 // Render the texture upside down into the back buffer 416 // Texcoords are chosen to flip the renderTarget about its Y axis. 417 float w = static_cast<float>(getWidth()); 418 float h = static_cast<float>(getHeight()); 419 float quad[4][6] = {{0 - 0.5f, 0 - 0.5f, 0.0f, 1.0f, 0.0f, 1.0f}, 420 {w - 0.5f, 0 - 0.5f, 0.0f, 1.0f, 1.0f, 1.0f}, 421 {w - 0.5f, h - 0.5f, 0.0f, 1.0f, 1.0f, 0.0f}, 422 {0 - 0.5f, h - 0.5f, 0.0f, 1.0f, 0.0f, 0.0f}}; // x, y, z, rhw, u, v 423 424 mDisplay->startScene(); 425 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float)); 426 427 restoreState(device); 428 429 mDisplay->endScene(); 430 431 HRESULT result = mSwapChain->Present(NULL, NULL, NULL, NULL, 0); 432 433 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR) 434 { 435 return error(EGL_BAD_ALLOC, false); 436 } 437 438 if (result == D3DERR_DEVICELOST) 439 { 440 return error(EGL_CONTEXT_LOST, false); 441 } 442 443 ASSERT(SUCCEEDED(result)); 444 445 checkForOutOfDateSwapChain(); 446 } 447 448 return true; 449 } 450 451 EGLint Surface::getWidth() const 452 { 453 return mWidth; 454 } 455 456 EGLint Surface::getHeight() const 457 { 458 return mHeight; 459 } 460 461 IDirect3DSurface9 *Surface::getRenderTarget() 462 { 463 IDirect3DSurface9 *textureSurface = NULL; 464 465 if (mFlipTexture) 466 { 467 mFlipTexture->GetSurfaceLevel(0, &textureSurface); 468 } 469 470 return textureSurface; 471 } 472 473 IDirect3DSurface9 *Surface::getDepthStencil() 474 { 475 if (mDepthStencil) 476 { 477 mDepthStencil->AddRef(); 478 } 479 480 return mDepthStencil; 481 } 482 483 void Surface::setSwapInterval(EGLint interval) 484 { 485 if (mSwapInterval == interval) 486 { 487 return; 488 } 489 490 mSwapInterval = interval; 491 mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval()); 492 mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval()); 493 494 mPresentInterval = convertInterval(mSwapInterval); 495 mPresentIntervalDirty = true; 496 } 497 } 498