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 25namespace egl 26{ 27 28Surface::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 50Surface::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 69Surface::~Surface() 70{ 71 unsubclassWindow(); 72 release(); 73} 74 75bool Surface::initialize() 76{ 77 if (!resetSwapChain()) 78 return false; 79 80 return true; 81} 82 83void Surface::release() 84{ 85 delete mSwapChain; 86 mSwapChain = NULL; 87 88 if (mTexture) 89 { 90 mTexture->releaseTexImage(); 91 mTexture = NULL; 92 } 93} 94 95bool 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 141bool 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 164bool 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 188bool 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 227HWND Surface::getWindowHandle() 228{ 229 return mWindow; 230} 231 232 233#define kSurfaceProperty _TEXT("Egl::SurfaceOwner") 234#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc") 235 236static 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 250void 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 277void 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 304bool 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 350bool Surface::swap() 351{ 352 return swapRect(0, 0, mWidth, mHeight); 353} 354 355bool 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 366EGLint Surface::isPostSubBufferSupported() const 367{ 368 return mPostSubBufferSupported; 369} 370 371rx::SwapChain *Surface::getSwapChain() const 372{ 373 return mSwapChain; 374} 375 376void 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 390EGLint Surface::getConfigID() const 391{ 392 return mConfig->mConfigID; 393} 394 395EGLint Surface::getWidth() const 396{ 397 return mWidth; 398} 399 400EGLint Surface::getHeight() const 401{ 402 return mHeight; 403} 404 405EGLint Surface::getPixelAspectRatio() const 406{ 407 return mPixelAspectRatio; 408} 409 410EGLenum Surface::getRenderBuffer() const 411{ 412 return mRenderBuffer; 413} 414 415EGLenum Surface::getSwapBehavior() const 416{ 417 return mSwapBehavior; 418} 419 420EGLenum Surface::getTextureFormat() const 421{ 422 return mTextureFormat; 423} 424 425EGLenum Surface::getTextureTarget() const 426{ 427 return mTextureTarget; 428} 429 430void Surface::setBoundTexture(gl::Texture2D *texture) 431{ 432 mTexture = texture; 433} 434 435gl::Texture2D *Surface::getBoundTexture() const 436{ 437 return mTexture; 438} 439 440EGLint Surface::isFixedSize() const 441{ 442 return mFixedSize; 443} 444 445EGLenum Surface::getFormat() const 446{ 447 return mConfig->mRenderTargetFormat; 448} 449} 450