diff options
Diffstat (limited to 'src/libEGL/Surface.cpp')
-rw-r--r-- | src/libEGL/Surface.cpp | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/src/libEGL/Surface.cpp b/src/libEGL/Surface.cpp new file mode 100644 index 00000000..539c4c62 --- /dev/null +++ b/src/libEGL/Surface.cpp @@ -0,0 +1,408 @@ +// +// Copyright (c) 2002-2012 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 "libGLESv2/renderer/SwapChain.h" +#include "libGLESv2/main.h" + +#include "libEGL/main.h" +#include "libEGL/Display.h" + +namespace egl +{ + +Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported) + : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported) +{ + mRenderer = mDisplay->getRenderer(); + mSwapChain = 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; + mWidth = -1; + mHeight = -1; + setSwapInterval(1); + + subclassWindow(); +} + +Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType) + : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE) +{ + mRenderer = mDisplay->getRenderer(); + mSwapChain = 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); +} + +Surface::~Surface() +{ + unsubclassWindow(); + release(); +} + +bool Surface::initialize() +{ + if (!resetSwapChain()) + return false; + + return true; +} + +void Surface::release() +{ + delete mSwapChain; + mSwapChain = NULL; + + if (mTexture) + { + mTexture->releaseTexImage(); + mTexture = NULL; + } +} + +bool Surface::resetSwapChain() +{ + ASSERT(!mSwapChain); + + int width; + int height; + + if (mWindow) + { + RECT windowRect; + if (!GetClientRect(getWindowHandle(), &windowRect)) + { + ASSERT(false); + + ERR("Could not retrieve the window dimensions"); + return error(EGL_BAD_SURFACE, false); + } + + width = windowRect.right - windowRect.left; + height = windowRect.bottom - windowRect.top; + } + else + { + // non-window surface - size is determined at creation + width = mWidth; + height = mHeight; + } + + mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle, + mConfig->mRenderTargetFormat, + mConfig->mDepthStencilFormat); + if (!mSwapChain) + { + return error(EGL_BAD_ALLOC, false); + } + + if (!resetSwapChain(width, height)) + { + delete mSwapChain; + mSwapChain = NULL; + return false; + } + + return true; +} + +bool Surface::resizeSwapChain(int backbufferWidth, int backbufferHeight) +{ + ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0); + ASSERT(mSwapChain); + + EGLint status = mSwapChain->resize(backbufferWidth, backbufferHeight); + + if (status == EGL_CONTEXT_LOST) + { + mDisplay->notifyDeviceLost(); + return false; + } + else if (status != EGL_SUCCESS) + { + return error(status, false); + } + + mWidth = backbufferWidth; + mHeight = backbufferHeight; + + return true; +} + +bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight) +{ + ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0); + ASSERT(mSwapChain); + + EGLint status = mSwapChain->reset(backbufferWidth, backbufferHeight, mSwapInterval); + + if (status == EGL_CONTEXT_LOST) + { + mRenderer->notifyDeviceLost(); + return false; + } + else if (status != EGL_SUCCESS) + { + return error(status, false); + } + + mWidth = backbufferWidth; + mHeight = backbufferHeight; + mSwapIntervalDirty = false; + + return true; +} + +bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) +{ + if (!mSwapChain) + { + return true; + } + + if (x + width > mWidth) + { + width = mWidth - x; + } + + if (y + height > mHeight) + { + height = mHeight - y; + } + + if (width == 0 || height == 0) + { + return true; + } + + EGLint status = mSwapChain->swapRect(x, y, width, height); + + if (status == EGL_CONTEXT_LOST) + { + mRenderer->notifyDeviceLost(); + return false; + } + else if (status != EGL_SUCCESS) + { + return error(status, false); + } + + checkForOutOfDateSwapChain(); + + return true; +} + +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; + } + + DWORD processId; + DWORD threadId = GetWindowThreadProcessId(mWindow, &processId); + if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId()) + { + return; + } + + SetLastError(0); + LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(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_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(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_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc); + ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(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 (mSwapIntervalDirty) + { + resetSwapChain(clientWidth, clientHeight); + } + else if (sizeDirty) + { + resizeSwapChain(clientWidth, clientHeight); + } + + if (mSwapIntervalDirty || sizeDirty) + { + if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this) + { + glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this); + } + + return true; + } + + return false; +} + +bool Surface::swap() +{ + return swapRect(0, 0, mWidth, mHeight); +} + +bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) +{ + if (!mPostSubBufferSupported) + { + // Spec is not clear about how this should be handled. + return true; + } + + return swapRect(x, y, width, height); +} + +EGLint Surface::getWidth() const +{ + return mWidth; +} + +EGLint Surface::getHeight() const +{ + return mHeight; +} + +EGLint Surface::isPostSubBufferSupported() const +{ + return mPostSubBufferSupported; +} + +rx::SwapChain *Surface::getSwapChain() const +{ + return mSwapChain; +} + +void Surface::setSwapInterval(EGLint interval) +{ + if (mSwapInterval == interval) + { + return; + } + + mSwapInterval = interval; + mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval()); + mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval()); + + mSwapIntervalDirty = 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; +} + +EGLenum Surface::getFormat() const +{ + return mConfig->mRenderTargetFormat; +} +} |