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