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