Home | History | Annotate | Download | only in libEGL
      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