1 // 2 // Copyright (c) 2002-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 // 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 <algorithm> 14 15 #include "libEGL/Surface.h" 16 17 #include "common/debug.h" 18 #include "libGLESv2/Texture.h" 19 #include "libGLESv2/renderer/SwapChain.h" 20 #include "libGLESv2/main.h" 21 22 #include "libEGL/main.h" 23 #include "libEGL/Display.h" 24 25 namespace egl 26 { 27 28 Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported) 29 : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported) 30 { 31 mRenderer = mDisplay->getRenderer(); 32 mSwapChain = NULL; 33 mShareHandle = NULL; 34 mTexture = NULL; 35 mTextureFormat = EGL_NO_TEXTURE; 36 mTextureTarget = EGL_NO_TEXTURE; 37 38 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio 39 mRenderBuffer = EGL_BACK_BUFFER; 40 mSwapBehavior = EGL_BUFFER_PRESERVED; 41 mSwapInterval = -1; 42 mWidth = -1; 43 mHeight = -1; 44 setSwapInterval(1); 45 46 subclassWindow(); 47 } 48 49 Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType) 50 : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE) 51 { 52 mRenderer = mDisplay->getRenderer(); 53 mSwapChain = NULL; 54 mWindowSubclassed = false; 55 mTexture = NULL; 56 mTextureFormat = textureFormat; 57 mTextureTarget = textureType; 58 59 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio 60 mRenderBuffer = EGL_BACK_BUFFER; 61 mSwapBehavior = EGL_BUFFER_PRESERVED; 62 mSwapInterval = -1; 63 setSwapInterval(1); 64 } 65 66 Surface::~Surface() 67 { 68 unsubclassWindow(); 69 release(); 70 } 71 72 bool Surface::initialize() 73 { 74 if (!resetSwapChain()) 75 return false; 76 77 return true; 78 } 79 80 void Surface::release() 81 { 82 delete mSwapChain; 83 mSwapChain = NULL; 84 85 if (mTexture) 86 { 87 mTexture->releaseTexImage(); 88 mTexture = NULL; 89 } 90 } 91 92 bool Surface::resetSwapChain() 93 { 94 ASSERT(!mSwapChain); 95 96 int width; 97 int height; 98 99 if (mWindow) 100 { 101 RECT windowRect; 102 if (!GetClientRect(getWindowHandle(), &windowRect)) 103 { 104 ASSERT(false); 105 106 ERR("Could not retrieve the window dimensions"); 107 return error(EGL_BAD_SURFACE, false); 108 } 109 110 width = windowRect.right - windowRect.left; 111 height = windowRect.bottom - windowRect.top; 112 } 113 else 114 { 115 // non-window surface - size is determined at creation 116 width = mWidth; 117 height = mHeight; 118 } 119 120 mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle, 121 mConfig->mRenderTargetFormat, 122 mConfig->mDepthStencilFormat); 123 if (!mSwapChain) 124 { 125 return error(EGL_BAD_ALLOC, false); 126 } 127 128 if (!resetSwapChain(width, height)) 129 { 130 delete mSwapChain; 131 mSwapChain = NULL; 132 return false; 133 } 134 135 return true; 136 } 137 138 bool Surface::resizeSwapChain(int backbufferWidth, int backbufferHeight) 139 { 140 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0); 141 ASSERT(mSwapChain); 142 143 EGLint status = mSwapChain->resize(backbufferWidth, backbufferHeight); 144 145 if (status == EGL_CONTEXT_LOST) 146 { 147 mDisplay->notifyDeviceLost(); 148 return false; 149 } 150 else if (status != EGL_SUCCESS) 151 { 152 return error(status, false); 153 } 154 155 mWidth = backbufferWidth; 156 mHeight = backbufferHeight; 157 158 return true; 159 } 160 161 bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight) 162 { 163 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0); 164 ASSERT(mSwapChain); 165 166 EGLint status = mSwapChain->reset(backbufferWidth, backbufferHeight, mSwapInterval); 167 168 if (status == EGL_CONTEXT_LOST) 169 { 170 mRenderer->notifyDeviceLost(); 171 return false; 172 } 173 else if (status != EGL_SUCCESS) 174 { 175 return error(status, false); 176 } 177 178 mWidth = backbufferWidth; 179 mHeight = backbufferHeight; 180 mSwapIntervalDirty = false; 181 182 return true; 183 } 184 185 bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) 186 { 187 if (!mSwapChain) 188 { 189 return true; 190 } 191 192 if (x + width > mWidth) 193 { 194 width = mWidth - x; 195 } 196 197 if (y + height > mHeight) 198 { 199 height = mHeight - y; 200 } 201 202 if (width == 0 || height == 0) 203 { 204 return true; 205 } 206 207 EGLint status = mSwapChain->swapRect(x, y, width, height); 208 209 if (status == EGL_CONTEXT_LOST) 210 { 211 mRenderer->notifyDeviceLost(); 212 return false; 213 } 214 else if (status != EGL_SUCCESS) 215 { 216 return error(status, false); 217 } 218 219 checkForOutOfDateSwapChain(); 220 221 return true; 222 } 223 224 HWND Surface::getWindowHandle() 225 { 226 return mWindow; 227 } 228 229 230 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner") 231 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc") 232 233 static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) 234 { 235 if (message == WM_SIZE) 236 { 237 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty)); 238 if(surf) 239 { 240 surf->checkForOutOfDateSwapChain(); 241 } 242 } 243 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc)); 244 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam); 245 } 246 247 void Surface::subclassWindow() 248 { 249 if (!mWindow) 250 { 251 return; 252 } 253 254 DWORD processId; 255 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId); 256 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId()) 257 { 258 return; 259 } 260 261 SetLastError(0); 262 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc)); 263 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS) 264 { 265 mWindowSubclassed = false; 266 return; 267 } 268 269 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this)); 270 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc)); 271 mWindowSubclassed = true; 272 } 273 274 void Surface::unsubclassWindow() 275 { 276 if(!mWindowSubclassed) 277 { 278 return; 279 } 280 281 // un-subclass 282 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc)); 283 284 // Check the windowproc is still SurfaceWindowProc. 285 // If this assert fails, then it is likely the application has subclassed the 286 // hwnd as well and did not unsubclass before destroying its EGL context. The 287 // application should be modified to either subclass before initializing the 288 // EGL context, or to unsubclass before destroying the EGL context. 289 if(parentWndFunc) 290 { 291 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc); 292 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc)); 293 } 294 295 RemoveProp(mWindow, kSurfaceProperty); 296 RemoveProp(mWindow, kParentWndProc); 297 mWindowSubclassed = false; 298 } 299 300 bool Surface::checkForOutOfDateSwapChain() 301 { 302 RECT client; 303 if (!GetClientRect(getWindowHandle(), &client)) 304 { 305 ASSERT(false); 306 return false; 307 } 308 309 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information. 310 int clientWidth = client.right - client.left; 311 int clientHeight = client.bottom - client.top; 312 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight(); 313 314 if (IsIconic(getWindowHandle())) 315 { 316 // The window is automatically resized to 150x22 when it's minimized, but the swapchain shouldn't be resized 317 // because that's not a useful size to render to. 318 sizeDirty = false; 319 } 320 321 if (mSwapIntervalDirty) 322 { 323 resetSwapChain(clientWidth, clientHeight); 324 } 325 else if (sizeDirty) 326 { 327 resizeSwapChain(clientWidth, clientHeight); 328 } 329 330 if (mSwapIntervalDirty || sizeDirty) 331 { 332 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this) 333 { 334 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this); 335 } 336 337 return true; 338 } 339 340 return false; 341 } 342 343 bool Surface::swap() 344 { 345 return swapRect(0, 0, mWidth, mHeight); 346 } 347 348 bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) 349 { 350 if (!mPostSubBufferSupported) 351 { 352 // Spec is not clear about how this should be handled. 353 return true; 354 } 355 356 return swapRect(x, y, width, height); 357 } 358 359 EGLint Surface::getWidth() const 360 { 361 return mWidth; 362 } 363 364 EGLint Surface::getHeight() const 365 { 366 return mHeight; 367 } 368 369 EGLint Surface::isPostSubBufferSupported() const 370 { 371 return mPostSubBufferSupported; 372 } 373 374 rx::SwapChain *Surface::getSwapChain() const 375 { 376 return mSwapChain; 377 } 378 379 void Surface::setSwapInterval(EGLint interval) 380 { 381 if (mSwapInterval == interval) 382 { 383 return; 384 } 385 386 mSwapInterval = interval; 387 mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval()); 388 mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval()); 389 390 mSwapIntervalDirty = true; 391 } 392 393 EGLenum Surface::getTextureFormat() const 394 { 395 return mTextureFormat; 396 } 397 398 EGLenum Surface::getTextureTarget() const 399 { 400 return mTextureTarget; 401 } 402 403 void Surface::setBoundTexture(gl::Texture2D *texture) 404 { 405 mTexture = texture; 406 } 407 408 gl::Texture2D *Surface::getBoundTexture() const 409 { 410 return mTexture; 411 } 412 413 EGLenum Surface::getFormat() const 414 { 415 return mConfig->mRenderTargetFormat; 416 } 417 } 418