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 "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