Home | History | Annotate | Download | only in libEGL
      1 //
      2 // Copyright (c) 2002-2010 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 
     17 #include "libEGL/main.h"
     18 #include "libEGL/Display.h"
     19 
     20 namespace egl
     21 {
     22 Surface::Surface(Display *display, const Config *config, HWND window)
     23     : mDisplay(display), mConfig(config), mWindow(window)
     24 {
     25     mSwapChain = NULL;
     26     mDepthStencil = NULL;
     27     mBackBuffer = NULL;
     28     mFlipTexture = NULL;
     29     mFlipState = NULL;
     30     mPreFlipState = NULL;
     31 
     32     mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
     33     mRenderBuffer = EGL_BACK_BUFFER;
     34     mSwapBehavior = EGL_BUFFER_PRESERVED;
     35     mSwapInterval = -1;
     36     setSwapInterval(1);
     37 
     38     subclassWindow();
     39     resetSwapChain();
     40 }
     41 
     42 Surface::~Surface()
     43 {
     44     unsubclassWindow();
     45     release();
     46 }
     47 
     48 void Surface::release()
     49 {
     50     if (mSwapChain)
     51     {
     52         mSwapChain->Release();
     53         mSwapChain = NULL;
     54     }
     55 
     56     if (mBackBuffer)
     57     {
     58         mBackBuffer->Release();
     59         mBackBuffer = NULL;
     60     }
     61 
     62     if (mDepthStencil)
     63     {
     64         mDepthStencil->Release();
     65         mDepthStencil = NULL;
     66     }
     67 
     68     if (mFlipTexture)
     69     {
     70         mFlipTexture->Release();
     71         mFlipTexture = NULL;
     72     }
     73 
     74     if (mFlipState)
     75     {
     76         mFlipState->Release();
     77         mFlipState = NULL;
     78     }
     79 
     80     if (mPreFlipState)
     81     {
     82         mPreFlipState->Release();
     83         mPreFlipState = NULL;
     84     }
     85 }
     86 
     87 void Surface::resetSwapChain()
     88 {
     89     RECT windowRect;
     90     if (!GetClientRect(getWindowHandle(), &windowRect))
     91     {
     92         ASSERT(false);
     93 
     94         ERR("Could not retrieve the window dimensions");
     95         return;
     96     }
     97 
     98     resetSwapChain(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
     99 }
    100 
    101 void Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
    102 {
    103     IDirect3DDevice9 *device = mDisplay->getDevice();
    104 
    105     if (device == NULL)
    106     {
    107         return;
    108     }
    109 
    110     // Evict all non-render target textures to system memory and release all resources
    111     // before reallocating them to free up as much video memory as possible.
    112     device->EvictManagedResources();
    113     release();
    114 
    115     D3DPRESENT_PARAMETERS presentParameters = {0};
    116 
    117     presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat;
    118     presentParameters.BackBufferCount = 1;
    119     presentParameters.BackBufferFormat = mConfig->mRenderTargetFormat;
    120     presentParameters.EnableAutoDepthStencil = FALSE;
    121     presentParameters.Flags = 0;
    122     presentParameters.hDeviceWindow = getWindowHandle();
    123     presentParameters.MultiSampleQuality = 0;                  // FIXME: Unimplemented
    124     presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;   // FIXME: Unimplemented
    125     presentParameters.PresentationInterval = mPresentInterval;
    126     presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
    127     presentParameters.Windowed = TRUE;
    128     presentParameters.BackBufferWidth = backbufferWidth;
    129     presentParameters.BackBufferHeight = backbufferHeight;
    130 
    131     HRESULT result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
    132 
    133     if (FAILED(result))
    134     {
    135         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    136 
    137         ERR("Could not create additional swap chains: %08lX", result);
    138         release();
    139         return error(EGL_BAD_ALLOC);
    140     }
    141 
    142     result = device->CreateDepthStencilSurface(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight,
    143                                                presentParameters.AutoDepthStencilFormat, presentParameters.MultiSampleType,
    144                                                presentParameters.MultiSampleQuality, FALSE, &mDepthStencil, NULL);
    145 
    146     if (FAILED(result))
    147     {
    148         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    149 
    150         ERR("Could not create depthstencil surface for new swap chain: %08lX", result);
    151         release();
    152         return error(EGL_BAD_ALLOC);
    153     }
    154 
    155     ASSERT(SUCCEEDED(result));
    156 
    157     result = device->CreateTexture(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 1, D3DUSAGE_RENDERTARGET,
    158                                    presentParameters.BackBufferFormat, D3DPOOL_DEFAULT, &mFlipTexture, NULL);
    159 
    160     if (FAILED(result))
    161     {
    162         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    163 
    164         ERR("Could not create flip texture for new swap chain: %08lX", result);
    165         release();
    166         return error(EGL_BAD_ALLOC);
    167     }
    168 
    169     mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
    170     mWidth = presentParameters.BackBufferWidth;
    171     mHeight = presentParameters.BackBufferHeight;
    172 
    173     mPresentIntervalDirty = false;
    174 
    175     InvalidateRect(mWindow, NULL, FALSE);
    176 
    177     // The flip state block recorded mFlipTexture so it is now invalid.
    178     releaseRecordedState(device);
    179 }
    180 
    181 HWND Surface::getWindowHandle()
    182 {
    183     return mWindow;
    184 }
    185 
    186 void Surface::writeRecordableFlipState(IDirect3DDevice9 *device)
    187 {
    188     // Disable all pipeline operations
    189     device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
    190     device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
    191     device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
    192     device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
    193     device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    194     device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
    195     device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
    196     device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
    197     device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
    198     device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
    199     device->SetPixelShader(NULL);
    200     device->SetVertexShader(NULL);
    201 
    202     // Just sample the texture
    203     device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    204     device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    205     device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
    206     device->SetTexture(0, NULL);   // The actual texture will change after resizing. But the pre-flip state block must save/restore the texture.
    207     device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
    208     device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
    209     device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
    210     device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
    211     device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
    212     device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
    213 
    214     RECT scissorRect = {0};   // Scissoring is disabled for flipping, but we need this to capture and restore the old rectangle
    215     device->SetScissorRect(&scissorRect);
    216     D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f};
    217     device->SetViewport(&viewport);
    218 }
    219 
    220 void Surface::applyFlipState(IDirect3DDevice9 *device)
    221 {
    222     HRESULT hr;
    223 
    224     if (mFlipState == NULL)
    225     {
    226         // Create two state blocks both recording the states that are changed when swapping.
    227 
    228         // mPreFlipState will record the original state each entry.
    229         hr = device->BeginStateBlock();
    230         ASSERT(SUCCEEDED(hr));
    231         writeRecordableFlipState(device);
    232         hr = device->EndStateBlock(&mPreFlipState);
    233         ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
    234 
    235         if (SUCCEEDED(hr))
    236         {
    237             mPreFlipState->Capture();
    238         }
    239 
    240         // mFlipState will record the state for the swap operation.
    241         hr = device->BeginStateBlock();
    242         ASSERT(SUCCEEDED(hr));
    243 
    244         writeRecordableFlipState(device);
    245 
    246         hr = device->EndStateBlock(&mFlipState);
    247         ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
    248 
    249         if (FAILED(hr))
    250         {
    251             mFlipState = NULL;
    252             mPreFlipState->Release();
    253             mPreFlipState = NULL;
    254         }
    255         else
    256         {
    257             hr = mFlipState->Apply();
    258             ASSERT(SUCCEEDED(hr));
    259         }
    260     }
    261     else
    262     {
    263         hr = mPreFlipState->Capture();
    264         ASSERT(SUCCEEDED(hr));
    265         hr = mFlipState->Apply();
    266         ASSERT(SUCCEEDED(hr));
    267     }
    268 
    269     device->GetRenderTarget(0, &mPreFlipBackBuffer);
    270     device->GetDepthStencilSurface(&mPreFlipDepthStencil);
    271 
    272     device->SetRenderTarget(0, mBackBuffer);
    273     device->SetDepthStencilSurface(NULL);
    274 }
    275 
    276 void Surface::restoreState(IDirect3DDevice9 *device)
    277 {
    278     device->SetRenderTarget(0, mPreFlipBackBuffer);
    279     device->SetDepthStencilSurface(mPreFlipDepthStencil);
    280 
    281     if (mPreFlipBackBuffer)
    282     {
    283         mPreFlipBackBuffer->Release();
    284         mPreFlipBackBuffer = NULL;
    285     }
    286 
    287     if (mPreFlipDepthStencil)
    288     {
    289         mPreFlipDepthStencil->Release();
    290         mPreFlipDepthStencil = NULL;
    291     }
    292 
    293     mPreFlipState->Apply();
    294 }
    295 
    296 // On the next flip, this will cause the state to be recorded from scratch.
    297 // In particular we need to do this if the flip texture changes.
    298 void Surface::releaseRecordedState(IDirect3DDevice9 *device)
    299 {
    300     if (mFlipState)
    301     {
    302         mFlipState->Release();
    303         mFlipState = NULL;
    304     }
    305 
    306     if (mPreFlipState)
    307     {
    308         mPreFlipState->Release();
    309         mPreFlipState = NULL;
    310     }
    311 }
    312 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
    313 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
    314 
    315 static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
    316   if (message == WM_SIZE) {
    317       Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
    318       if(surf) {
    319         surf->checkForOutOfDateSwapChain();
    320       }
    321   }
    322   WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
    323   return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
    324 }
    325 
    326 void Surface::subclassWindow()
    327 {
    328   SetLastError(0);
    329   LONG oldWndProc = SetWindowLong(mWindow, GWL_WNDPROC, reinterpret_cast<LONG>(SurfaceWindowProc));
    330   if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS) {
    331     mWindowSubclassed = false;
    332     return;
    333   }
    334 
    335   SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
    336   SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
    337   mWindowSubclassed = true;
    338 }
    339 
    340 void Surface::unsubclassWindow()
    341 {
    342   if(!mWindowSubclassed)
    343     return;
    344 
    345   // un-subclass
    346   LONG parentWndFunc = reinterpret_cast<LONG>(GetProp(mWindow, kParentWndProc));
    347 
    348   // Check the windowproc is still SurfaceWindowProc.
    349   // If this assert fails, then it is likely the application has subclassed the
    350   // hwnd as well and did not unsubclass before destroying its EGL context. The
    351   // application should be modified to either subclass before initializing the
    352   // EGL context, or to unsubclass before destroying the EGL context.
    353   if(parentWndFunc) {
    354     LONG prevWndFunc = SetWindowLong(mWindow, GWL_WNDPROC, parentWndFunc);
    355     ASSERT(prevWndFunc == reinterpret_cast<LONG>(SurfaceWindowProc));
    356   }
    357 
    358   RemoveProp(mWindow, kSurfaceProperty);
    359   RemoveProp(mWindow, kParentWndProc);
    360   mWindowSubclassed = false;
    361 }
    362 
    363 bool Surface::checkForOutOfDateSwapChain()
    364 {
    365     RECT client;
    366     if (!GetClientRect(getWindowHandle(), &client))
    367     {
    368         ASSERT(false);
    369         return false;
    370     }
    371 
    372     // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
    373     int clientWidth = client.right - client.left;
    374     int clientHeight = client.bottom - client.top;
    375     bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
    376 
    377     if (sizeDirty || mPresentIntervalDirty)
    378     {
    379         resetSwapChain(clientWidth, clientHeight);
    380         if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
    381         {
    382             glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
    383         }
    384 
    385         return true;
    386     }
    387     return false;
    388 }
    389 
    390 DWORD Surface::convertInterval(EGLint interval)
    391 {
    392     switch(interval)
    393     {
    394       case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
    395       case 1: return D3DPRESENT_INTERVAL_ONE;
    396       case 2: return D3DPRESENT_INTERVAL_TWO;
    397       case 3: return D3DPRESENT_INTERVAL_THREE;
    398       case 4: return D3DPRESENT_INTERVAL_FOUR;
    399       default: UNREACHABLE();
    400     }
    401 
    402     return D3DPRESENT_INTERVAL_DEFAULT;
    403 }
    404 
    405 
    406 bool Surface::swap()
    407 {
    408     if (mSwapChain)
    409     {
    410         IDirect3DDevice9 *device = mDisplay->getDevice();
    411 
    412         applyFlipState(device);
    413         device->SetTexture(0, mFlipTexture);
    414 
    415         // Render the texture upside down into the back buffer
    416         // Texcoords are chosen to flip the renderTarget about its Y axis.
    417         float w = static_cast<float>(getWidth());
    418         float h = static_cast<float>(getHeight());
    419         float quad[4][6] = {{0 - 0.5f, 0 - 0.5f, 0.0f, 1.0f, 0.0f, 1.0f},
    420                             {w - 0.5f, 0 - 0.5f, 0.0f, 1.0f, 1.0f, 1.0f},
    421                             {w - 0.5f, h - 0.5f, 0.0f, 1.0f, 1.0f, 0.0f},
    422                             {0 - 0.5f, h - 0.5f, 0.0f, 1.0f, 0.0f, 0.0f}};   // x, y, z, rhw, u, v
    423 
    424         mDisplay->startScene();
    425         device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
    426 
    427         restoreState(device);
    428 
    429         mDisplay->endScene();
    430 
    431         HRESULT result = mSwapChain->Present(NULL, NULL, NULL, NULL, 0);
    432 
    433         if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
    434         {
    435             return error(EGL_BAD_ALLOC, false);
    436         }
    437 
    438         if (result == D3DERR_DEVICELOST)
    439         {
    440             return error(EGL_CONTEXT_LOST, false);
    441         }
    442 
    443         ASSERT(SUCCEEDED(result));
    444 
    445         checkForOutOfDateSwapChain();
    446     }
    447 
    448     return true;
    449 }
    450 
    451 EGLint Surface::getWidth() const
    452 {
    453     return mWidth;
    454 }
    455 
    456 EGLint Surface::getHeight() const
    457 {
    458     return mHeight;
    459 }
    460 
    461 IDirect3DSurface9 *Surface::getRenderTarget()
    462 {
    463     IDirect3DSurface9 *textureSurface = NULL;
    464 
    465     if (mFlipTexture)
    466     {
    467         mFlipTexture->GetSurfaceLevel(0, &textureSurface);
    468     }
    469 
    470     return textureSurface;
    471 }
    472 
    473 IDirect3DSurface9 *Surface::getDepthStencil()
    474 {
    475     if (mDepthStencil)
    476     {
    477         mDepthStencil->AddRef();
    478     }
    479 
    480     return mDepthStencil;
    481 }
    482 
    483 void Surface::setSwapInterval(EGLint interval)
    484 {
    485     if (mSwapInterval == interval)
    486     {
    487         return;
    488     }
    489 
    490     mSwapInterval = interval;
    491     mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval());
    492     mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval());
    493 
    494     mPresentInterval = convertInterval(mSwapInterval);
    495     mPresentIntervalDirty = true;
    496 }
    497 }
    498