Edit

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

Branch :

  • Show log

    Commit

  • Author : apatrick@chromium.org
    Date : 2010-09-20 19:06:56
    Hash : e860f079
    Message : Trying to fix bug crash when eglCreateWindowSurface (or any other code that causes createDevice to be invoked) is called and CreateDevice reports DEVICELOST or NOTAVAILABLE. This was a Windows XP service pack 3 box. 0x10002545 [libegl.dll - display.cpp:340] egl::Display::createDevice() 0x10002912 [libegl.dll - display.cpp:468] egl::Display::getDevice() 0x01d6a80f [chrome.dll - gpu_info_collector_win.cc:29] gpu_info_collector::CollectGraphicsInfo(GPUInfo *) 0x01d68eca [chrome.dll - gpu_thread.cc:104] GpuThread::OnEstablishChannel(int) Previously createDevice only checked for out of memory and the ASSERT(SUCCEEDED(result)) has no effect in release builds. I simulated the failure in a debugger and discovered a second place where a null dereference would occur in this case. I don't think this crash is technically correct but it prevents crashes. Review URL: http://codereview.appspot.com/2217043 git-svn-id: https://angleproject.googlecode.com/svn/trunk@428 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 "libEGL/Surface.h"
    
    #include "common/debug.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;
        mBackBuffer = NULL;
        mRenderTarget = NULL;
        mFlipTexture = NULL;
        mFlipState = NULL;
        mPreFlipState = NULL;
    
        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);
    
        resetSwapChain();
    }
    
    Surface::~Surface()
    {
        release();
    }
    
    void Surface::release()
    {
        if (mSwapChain)
        {
            mSwapChain->Release();
            mSwapChain = NULL;
        }
    
        if (mBackBuffer)
        {
            mBackBuffer->Release();
            mBackBuffer = NULL;
        }
    
        if (mRenderTarget)
        {
            mRenderTarget->Release();
            mRenderTarget = NULL;
        }
    
        if (mDepthStencil)
        {
            mDepthStencil->Release();
            mDepthStencil = NULL;
        }
    
        if (mFlipTexture)
        {
            mFlipTexture->Release();
            mFlipTexture = NULL;
        }
    
        if (mFlipState)
        {
            mFlipState->Release();
            mFlipState = NULL;
        }
    
        if (mPreFlipState)
        {
            mPreFlipState->Release();
            mPreFlipState = NULL;
        }
    }
    
    void Surface::resetSwapChain()
    {
        IDirect3DDevice9 *device = mDisplay->getDevice();
    
        if (device == NULL)
        {
            return;
        }
        
        D3DPRESENT_PARAMETERS presentParameters = {0};
    
        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;
    
        RECT windowRect;
        if (!GetClientRect(getWindowHandle(), &windowRect))
        {
            ASSERT(false);
            return;
        }
    
        presentParameters.BackBufferWidth = windowRect.right - windowRect.left;
        presentParameters.BackBufferHeight = windowRect.bottom - windowRect.top;
    
        IDirect3DSwapChain9 *swapChain = NULL;
        HRESULT result = device->CreateAdditionalSwapChain(&presentParameters, &swapChain);
    
        if (FAILED(result))
        {
            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
            ERR("Could not create additional swap chains: %08lX", result);
            return error(EGL_BAD_ALLOC);
        }
    
        IDirect3DSurface9 *depthStencilSurface = NULL;
        result = device->CreateDepthStencilSurface(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight,
                                                   presentParameters.AutoDepthStencilFormat, presentParameters.MultiSampleType,
                                                   presentParameters.MultiSampleQuality, FALSE, &depthStencilSurface, NULL);
    
        if (FAILED(result))
        {
            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
            swapChain->Release();
    
            ERR("Could not create depthstencil surface for new swap chain: %08lX", result);
            return error(EGL_BAD_ALLOC);
        }
    
        IDirect3DSurface9 *renderTarget = NULL;
        result = device->CreateRenderTarget(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, presentParameters.BackBufferFormat,
                                            presentParameters.MultiSampleType, presentParameters.MultiSampleQuality, FALSE, &renderTarget, NULL);
    
        if (FAILED(result))
        {
            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
            swapChain->Release();
            depthStencilSurface->Release();
    
            ERR("Could not create render target surface for new swap chain: %08lX", result);
            return error(EGL_BAD_ALLOC);
        }
    
        ASSERT(SUCCEEDED(result));
    
        IDirect3DTexture9 *flipTexture = NULL;
        result = device->CreateTexture(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 1, D3DUSAGE_RENDERTARGET,
                                       presentParameters.BackBufferFormat, D3DPOOL_DEFAULT, &flipTexture, NULL);
    
        if (FAILED(result))
        {
            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
            swapChain->Release();
            depthStencilSurface->Release();
            renderTarget->Release();
    
            ERR("Could not create flip texture for new swap chain: %08lX", result);
            return error(EGL_BAD_ALLOC);
        }
    
        IDirect3DSurface9 *backBuffer = NULL;
        swapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backBuffer);
    
        if (mSwapChain) mSwapChain->Release();
        if (mDepthStencil) mDepthStencil->Release();
        if (mBackBuffer) mBackBuffer->Release();
        if (mRenderTarget) mRenderTarget->Release();
        if (mFlipTexture) mFlipTexture->Release();
    
        mWidth = presentParameters.BackBufferWidth;
        mHeight = presentParameters.BackBufferHeight;
    
        mSwapChain = swapChain;
        mDepthStencil = depthStencilSurface;
        mBackBuffer = backBuffer;
        mRenderTarget = renderTarget;
        mFlipTexture = flipTexture;
    
        mPresentIntervalDirty = false;
    
        // The flip state block recorded mFlipTexture so it is now invalid.
        releaseRecordedState(device);
    }
    
    HWND Surface::getWindowHandle()
    {
        return mWindow;
    }
    
    void Surface::writeRecordableFlipState(IDirect3DDevice9 *device)
    {
        // Disable all pipeline operations
        device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
        device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
        device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
        device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
        device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
        device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
        device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
        device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
        device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
        device->SetPixelShader(NULL);
        device->SetVertexShader(NULL);
    
        // Just sample the texture
        device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
        device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
        device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
        device->SetTexture(0, NULL); // The actual texture will change after resizing. But the pre-flip state block must save/restore the texture.
        device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
        device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
        device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
        device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
        device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
        device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
    
        device->SetStreamSourceFreq(0, 1); // DrawPrimitiveUP only cares about stream 0, not the rest.
    }
    
    void Surface::applyFlipState(IDirect3DDevice9 *device)
    {
        HRESULT hr;
    
        if (mFlipState == NULL)
        {
            // Create two state blocks both recording the states that are changed when swapping.
    
            // mPreFlipState will record the original state each entry.
            hr = device->BeginStateBlock();
            ASSERT(SUCCEEDED(hr));
            writeRecordableFlipState(device);
            hr = device->EndStateBlock(&mPreFlipState);
            ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
    
            if (SUCCEEDED(hr))
            {
                mPreFlipState->Capture();
            }
    
            // mFlipState will record the state for the swap operation.
            hr = device->BeginStateBlock();
            ASSERT(SUCCEEDED(hr));
    
            writeRecordableFlipState(device);
    
            hr = device->EndStateBlock(&mFlipState);
            ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
    
            if (FAILED(hr))
            {
                mFlipState = NULL;
                mPreFlipState->Release();
                mPreFlipState = NULL;
            }
            else
            {
                hr = mFlipState->Apply();
                ASSERT(SUCCEEDED(hr));
            }
        }
        else
        {
            hr = mPreFlipState->Capture();
            ASSERT(SUCCEEDED(hr));
            hr = mFlipState->Apply();
            ASSERT(SUCCEEDED(hr));
        }
    
        device->GetRenderTarget(0, &mPreFlipBackBuffer);
        device->GetDepthStencilSurface(&mPreFlipDepthStencil);
    
        device->SetRenderTarget(0, mBackBuffer);
        device->SetDepthStencilSurface(NULL);
    }
    
    void Surface::restoreState(IDirect3DDevice9 *device)
    {
        mPreFlipState->Apply();
    
        device->SetRenderTarget(0, mPreFlipBackBuffer);
        device->SetDepthStencilSurface(mPreFlipDepthStencil);
    
        if (mPreFlipBackBuffer)
        {
            mPreFlipBackBuffer->Release();
            mPreFlipBackBuffer = NULL;
        }
    
        if (mPreFlipDepthStencil)
        {
            mPreFlipDepthStencil->Release();
            mPreFlipDepthStencil = NULL;
        }
    }
    
    // On the next flip, this will cause the state to be recorded from scratch.
    // In particular we need to do this if the flip texture changes.
    void Surface::releaseRecordedState(IDirect3DDevice9 *device)
    {
        if (mFlipState)
        {
            mFlipState->Release();
            mFlipState = NULL;
        }
    
        if (mPreFlipState)
        {
            mPreFlipState->Release();
            mPreFlipState = NULL;
        }
    }
    
    bool Surface::checkForOutOfDateSwapChain()
    {
        RECT client;
        if (!GetClientRect(getWindowHandle(), &client))
        {
            ASSERT(false);
            return false;
        }
    
        if (getWidth() != client.right - client.left || getHeight() != client.bottom - client.top || mPresentIntervalDirty)
        {
            resetSwapChain();
    
            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)
        {
            IDirect3DTexture9 *flipTexture = mFlipTexture;
            flipTexture->AddRef();
    
            IDirect3DSurface9 *renderTarget = mRenderTarget;
            renderTarget->AddRef();
    
            EGLint oldWidth = mWidth;
            EGLint oldHeight = mHeight;
    
            checkForOutOfDateSwapChain();
    
            IDirect3DDevice9 *device = mDisplay->getDevice();
    
            IDirect3DSurface9 *textureSurface;
            flipTexture->GetSurfaceLevel(0, &textureSurface);
    
            mDisplay->endScene();
            device->StretchRect(renderTarget, NULL, textureSurface, NULL, D3DTEXF_NONE);
            renderTarget->Release();
    
            applyFlipState(device);
            device->SetTexture(0, flipTexture);
    
            float xscale = (float)mWidth / oldWidth;
            float yscale = (float)mHeight / oldHeight;
    
            // Render the texture upside down into the back buffer
            // Texcoords are chosen to pin a potentially resized image into the upper-left corner without scaling.
            float quad[4][6] = {{     0 - 0.5f,       0 - 0.5f, 0.0f, 1.0f, 0.0f,   1.0f       },
                                {mWidth - 0.5f,       0 - 0.5f, 0.0f, 1.0f, xscale, 1.0f       },
                                {mWidth - 0.5f, mHeight - 0.5f, 0.0f, 1.0f, xscale, 1.0f-yscale},
                                {     0 - 0.5f, mHeight - 0.5f, 0.0f, 1.0f, 0.0f,   1.0f-yscale}};   // x, y, z, rhw, u, v
    
            mDisplay->startScene();
            device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
    
            flipTexture->Release();
            textureSurface->Release();
    
            restoreState(device);
    
            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));
    
        }
    
        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;
    }
    
    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;
    }
    }