Edit

kc3-lang/angle/src/libEGL/Surface.cpp

Branch :

  • Show log

    Commit

  • Author : apatrick@chromium.org
    Date : 2011-05-05 00:00:30
    Hash : 192a1810
    Message : Implemented semantics for eglDestroySurface when surface is current on thread. The spec has this to say. " All resources associated with surface which were allocated by EGL are marked for deletion as soon as possible. If surface is current to any thread (see section 3.7.3), resources are not actually released while the surface remains current. Future references to surface remain valid only so long as it is current; it will be destroyed, and all future references to it will become invalid, as soon as any otherwise valid eglMakeCurrent call is made from the thread it is bound to." Review URL: http://codereview.appspot.com/4449064 git-svn-id: https://angleproject.googlecode.com/svn/trunk@632 736b8ea6-26fd-11df-bfd4-992fa37f6226

  • src/libEGL/Surface.cpp
  • //
    // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // Surface.cpp: Implements the egl::Surface class, representing a drawing surface
    // such as the client area of a window, including any back buffers.
    // Implements EGLSurface and related functionality. [EGL 1.4] section 2.2 page 3.
    
    #include <tchar.h>
    
    #include "libEGL/Surface.h"
    
    #include "common/debug.h"
    #include "libGLESv2/Texture.h"
    
    #include "libEGL/main.h"
    #include "libEGL/Display.h"
    
    namespace egl
    {
    Surface::Surface(Display *display, const Config *config, HWND window) 
        : mDisplay(display), mConfig(config), mWindow(window)
    {
        mSwapChain = NULL;
        mDepthStencil = NULL;
        mRenderTarget = NULL;
        mOffscreenTexture = NULL;
        mShareHandle = NULL;
        mTexture = NULL;
        mTextureFormat = EGL_NO_TEXTURE;
        mTextureTarget = EGL_NO_TEXTURE;
    
        mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
        mRenderBuffer = EGL_BACK_BUFFER;
        mSwapBehavior = EGL_BUFFER_PRESERVED;
        mSwapInterval = -1;
        setSwapInterval(1);
    
        mIsPendingDestroy = false;
    
        subclassWindow();
        resetSwapChain();
    }
    
    Surface::Surface(Display *display, const Config *config, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
        : mDisplay(display), mWindow(NULL), mConfig(config), mWidth(width), mHeight(height)
    {
        mSwapChain = NULL;
        mDepthStencil = NULL;
        mRenderTarget = NULL;
        mOffscreenTexture = NULL;
        mShareHandle = NULL;
        mWindowSubclassed = false;
        mTexture = NULL;
        mTextureFormat = textureFormat;
        mTextureTarget = textureType;
    
        mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
        mRenderBuffer = EGL_BACK_BUFFER;
        mSwapBehavior = EGL_BUFFER_PRESERVED;
        mSwapInterval = -1;
        setSwapInterval(1);
    
        mIsPendingDestroy = false;
    
        resetSwapChain(width, height);
    }
    
    Surface::~Surface()
    {
        unsubclassWindow();
        release();
    }
    
    void Surface::release()
    {
        if (mSwapChain)
        {
            mSwapChain->Release();
            mSwapChain = NULL;
        }
    
        if (mDepthStencil)
        {
            mDepthStencil->Release();
            mDepthStencil = NULL;
        }
    
        if (mRenderTarget)
        {
            mRenderTarget->Release();
            mRenderTarget = NULL;
        }
    
        if (mOffscreenTexture)
        {
            mOffscreenTexture->Release();
            mOffscreenTexture = NULL;
        }
    
        if (mTexture)
        {
            mTexture->releaseTexImage();
            mTexture = NULL;
        }
    }
    
    void Surface::resetSwapChain()
    {
        if (!mWindow) {
            resetSwapChain(mWidth, mHeight);
            return;
        }
    
        RECT windowRect;
        if (!GetClientRect(getWindowHandle(), &windowRect))
        {
            ASSERT(false);
    
            ERR("Could not retrieve the window dimensions");
            return;
        }
    
        resetSwapChain(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
    }
    
    void Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
    {
        IDirect3DDevice9 *device = mDisplay->getDevice();
    
        if (device == NULL)
        {
            return;
        }
    
        // Evict all non-render target textures to system memory and release all resources
        // before reallocating them to free up as much video memory as possible.
        device->EvictManagedResources();
        release();
    
        D3DPRESENT_PARAMETERS presentParameters = {0};
        HRESULT result;
    
        presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat;
        presentParameters.BackBufferCount = 1;
        presentParameters.BackBufferFormat = mConfig->mRenderTargetFormat;
        presentParameters.EnableAutoDepthStencil = FALSE;
        presentParameters.Flags = 0;
        presentParameters.hDeviceWindow = getWindowHandle();
        presentParameters.MultiSampleQuality = 0;                  // FIXME: Unimplemented
        presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;   // FIXME: Unimplemented
        presentParameters.PresentationInterval = mPresentInterval;
        presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
        presentParameters.Windowed = TRUE;
        presentParameters.BackBufferWidth = backbufferWidth;
        presentParameters.BackBufferHeight = backbufferHeight;
    
        if (mWindow)
        {
            result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
        } else {
            HANDLE *pShareHandle = NULL;
            if (mDisplay->isD3d9ExDevice()) {
                pShareHandle = &mShareHandle;
            }
    
            result = device->CreateTexture(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 1, D3DUSAGE_RENDERTARGET,
                                           presentParameters.BackBufferFormat, D3DPOOL_DEFAULT, &mOffscreenTexture, pShareHandle);
        }
    
        if (FAILED(result))
        {
            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
            ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
            release();
            return error(EGL_BAD_ALLOC);
        }
    
        if (mConfig->mDepthStencilFormat != D3DFMT_UNKNOWN)
        {
            result = device->CreateDepthStencilSurface(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight,
                                                       presentParameters.AutoDepthStencilFormat, presentParameters.MultiSampleType,
                                                       presentParameters.MultiSampleQuality, FALSE, &mDepthStencil, NULL);
        }
    
        if (FAILED(result))
        {
            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
            ERR("Could not create depthstencil surface for new swap chain: %08lX", result);
            release();
            return error(EGL_BAD_ALLOC);
        }
    
        if (mWindow) {
            mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mRenderTarget);
            InvalidateRect(mWindow, NULL, FALSE);
        } else {
            mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
        }
    
        mWidth = presentParameters.BackBufferWidth;
        mHeight = presentParameters.BackBufferHeight;
    
        mPresentIntervalDirty = false;
    }
    
    HWND Surface::getWindowHandle()
    {
        return mWindow;
    }
    
    
    #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
    #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
    
    static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
      if (message == WM_SIZE) {
          Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
          if(surf) {
            surf->checkForOutOfDateSwapChain();
          }
      }
      WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
      return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
    }
    
    void Surface::subclassWindow()
    {
      if (!mWindow)
        return;
    
      SetLastError(0);
      LONG oldWndProc = SetWindowLong(mWindow, GWL_WNDPROC, reinterpret_cast<LONG>(SurfaceWindowProc));
      if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS) {
        mWindowSubclassed = false;
        return;
      }
    
      SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
      SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
      mWindowSubclassed = true;
    }
    
    void Surface::unsubclassWindow()
    {
      if(!mWindowSubclassed)
        return;
    
      // un-subclass
      LONG parentWndFunc = reinterpret_cast<LONG>(GetProp(mWindow, kParentWndProc));
    
      // Check the windowproc is still SurfaceWindowProc.
      // If this assert fails, then it is likely the application has subclassed the
      // hwnd as well and did not unsubclass before destroying its EGL context. The
      // application should be modified to either subclass before initializing the
      // EGL context, or to unsubclass before destroying the EGL context.
      if(parentWndFunc) {
        LONG prevWndFunc = SetWindowLong(mWindow, GWL_WNDPROC, parentWndFunc);
        ASSERT(prevWndFunc == reinterpret_cast<LONG>(SurfaceWindowProc));
      }
    
      RemoveProp(mWindow, kSurfaceProperty);
      RemoveProp(mWindow, kParentWndProc);
      mWindowSubclassed = false;
    }
    
    bool Surface::checkForOutOfDateSwapChain()
    {
        RECT client;
        if (!GetClientRect(getWindowHandle(), &client))
        {
            ASSERT(false);
            return false;
        }
    
        // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
        int clientWidth = client.right - client.left;
        int clientHeight = client.bottom - client.top;
        bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
    
        if (sizeDirty || mPresentIntervalDirty)
        {
            resetSwapChain(clientWidth, clientHeight);
            if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
            {
                glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
            }
    
            return true;
        }
        return false;
    }
    
    DWORD Surface::convertInterval(EGLint interval)
    {
        switch(interval)
        {
          case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
          case 1: return D3DPRESENT_INTERVAL_ONE;
          case 2: return D3DPRESENT_INTERVAL_TWO;
          case 3: return D3DPRESENT_INTERVAL_THREE;
          case 4: return D3DPRESENT_INTERVAL_FOUR;
          default: UNREACHABLE();
        }
    
        return D3DPRESENT_INTERVAL_DEFAULT;
    }
    
    
    bool Surface::swap()
    {
        if (mSwapChain)
        {
            mDisplay->endScene();
    
            HRESULT result = mSwapChain->Present(NULL, NULL, NULL, NULL, 0);
    
            if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
            {
                return error(EGL_BAD_ALLOC, false);
            }
    
            if (result == D3DERR_DEVICELOST)
            {
                return error(EGL_CONTEXT_LOST, false);
            }
    
            ASSERT(SUCCEEDED(result));
    
            checkForOutOfDateSwapChain();
        }
    
        return true;
    }
    
    EGLint Surface::getWidth() const
    {
        return mWidth;
    }
    
    EGLint Surface::getHeight() const
    {
        return mHeight;
    }
    
    IDirect3DSurface9 *Surface::getRenderTarget()
    {
        if (mRenderTarget)
        {
            mRenderTarget->AddRef();
        }
    
        return mRenderTarget;
    }
    
    IDirect3DSurface9 *Surface::getDepthStencil()
    {
        if (mDepthStencil)
        {
            mDepthStencil->AddRef();
        }
    
        return mDepthStencil;
    }
    
    IDirect3DTexture9 *Surface::getOffscreenTexture()
    {
        if (mOffscreenTexture)
        {
            mOffscreenTexture->AddRef();
        }
    
        return mOffscreenTexture;
    }
    
    void Surface::setSwapInterval(EGLint interval)
    {
        if (mSwapInterval == interval)
        {
            return;
        }
        
        mSwapInterval = interval;
        mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval());
        mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval());
    
        mPresentInterval = convertInterval(mSwapInterval);
        mPresentIntervalDirty = true;
    }
    
    EGLenum Surface::getTextureFormat() const
    {
        return mTextureFormat;
    }
    
    EGLenum Surface::getTextureTarget() const
    {
        return mTextureTarget;
    }
    
    void Surface::setBoundTexture(gl::Texture2D *texture)
    {
        mTexture = texture;
    }
    
    gl::Texture2D *Surface::getBoundTexture() const
    {
        return mTexture;
    }
    
    D3DFORMAT Surface::getFormat() const
    {
        return mConfig->mRenderTargetFormat;
    }
    
    void Surface::setPendingDestroy() {
        mIsPendingDestroy = true;
    }
    
    bool Surface::isPendingDestroy() const {
        return mIsPendingDestroy;
    }
    
    }