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