michael@0: // michael@0: // Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: // Surface.cpp: Implements the egl::Surface class, representing a drawing surface michael@0: // such as the client area of a window, including any back buffers. michael@0: // Implements EGLSurface and related functionality. [EGL 1.4] section 2.2 page 3. michael@0: michael@0: #include michael@0: michael@0: #include "libEGL/Surface.h" michael@0: michael@0: #include "common/debug.h" michael@0: #include "libGLESv2/Texture.h" michael@0: #include "libGLESv2/renderer/SwapChain.h" michael@0: #include "libGLESv2/main.h" michael@0: michael@0: #include "libEGL/main.h" michael@0: #include "libEGL/Display.h" michael@0: michael@0: #include michael@0: michael@0: namespace egl michael@0: { michael@0: michael@0: Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported) michael@0: : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported) michael@0: { michael@0: mRenderer = mDisplay->getRenderer(); michael@0: mSwapChain = NULL; michael@0: mShareHandle = NULL; michael@0: mTexture = NULL; michael@0: mTextureFormat = EGL_NO_TEXTURE; michael@0: mTextureTarget = EGL_NO_TEXTURE; michael@0: michael@0: mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio michael@0: mRenderBuffer = EGL_BACK_BUFFER; michael@0: mSwapBehavior = EGL_BUFFER_PRESERVED; michael@0: mSwapInterval = -1; michael@0: mWidth = -1; michael@0: mHeight = -1; michael@0: setSwapInterval(1); michael@0: michael@0: subclassWindow(); michael@0: } michael@0: michael@0: Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType) michael@0: : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE) michael@0: { michael@0: mRenderer = mDisplay->getRenderer(); michael@0: mSwapChain = NULL; michael@0: mWindowSubclassed = false; michael@0: mTexture = NULL; michael@0: mTextureFormat = textureFormat; michael@0: mTextureTarget = textureType; michael@0: michael@0: mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio michael@0: mRenderBuffer = EGL_BACK_BUFFER; michael@0: mSwapBehavior = EGL_BUFFER_PRESERVED; michael@0: mSwapInterval = -1; michael@0: setSwapInterval(1); michael@0: } michael@0: michael@0: Surface::~Surface() michael@0: { michael@0: unsubclassWindow(); michael@0: release(); michael@0: } michael@0: michael@0: bool Surface::initialize() michael@0: { michael@0: if (!resetSwapChain()) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void Surface::release() michael@0: { michael@0: delete mSwapChain; michael@0: mSwapChain = NULL; michael@0: michael@0: if (mTexture) michael@0: { michael@0: mTexture->releaseTexImage(); michael@0: mTexture = NULL; michael@0: } michael@0: } michael@0: michael@0: bool Surface::resetSwapChain() michael@0: { michael@0: ASSERT(!mSwapChain); michael@0: michael@0: int width; michael@0: int height; michael@0: michael@0: if (mWindow) michael@0: { michael@0: RECT windowRect; michael@0: if (!GetClientRect(getWindowHandle(), &windowRect)) michael@0: { michael@0: ASSERT(false); michael@0: michael@0: ERR("Could not retrieve the window dimensions"); michael@0: return error(EGL_BAD_SURFACE, false); michael@0: } michael@0: michael@0: width = windowRect.right - windowRect.left; michael@0: height = windowRect.bottom - windowRect.top; michael@0: } michael@0: else michael@0: { michael@0: // non-window surface - size is determined at creation michael@0: width = mWidth; michael@0: height = mHeight; michael@0: } michael@0: michael@0: mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle, michael@0: mConfig->mRenderTargetFormat, michael@0: mConfig->mDepthStencilFormat); michael@0: if (!mSwapChain) michael@0: { michael@0: return error(EGL_BAD_ALLOC, false); michael@0: } michael@0: michael@0: if (!resetSwapChain(width, height)) michael@0: { michael@0: delete mSwapChain; michael@0: mSwapChain = NULL; michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool Surface::resizeSwapChain(int backbufferWidth, int backbufferHeight) michael@0: { michael@0: ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0); michael@0: ASSERT(mSwapChain); michael@0: michael@0: EGLint status = mSwapChain->resize(backbufferWidth, backbufferHeight); michael@0: michael@0: if (status == EGL_CONTEXT_LOST) michael@0: { michael@0: mDisplay->notifyDeviceLost(); michael@0: return false; michael@0: } michael@0: else if (status != EGL_SUCCESS) michael@0: { michael@0: return error(status, false); michael@0: } michael@0: michael@0: mWidth = backbufferWidth; michael@0: mHeight = backbufferHeight; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight) michael@0: { michael@0: ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0); michael@0: ASSERT(mSwapChain); michael@0: michael@0: EGLint status = mSwapChain->reset(backbufferWidth, backbufferHeight, mSwapInterval); michael@0: michael@0: if (status == EGL_CONTEXT_LOST) michael@0: { michael@0: mRenderer->notifyDeviceLost(); michael@0: return false; michael@0: } michael@0: else if (status != EGL_SUCCESS) michael@0: { michael@0: return error(status, false); michael@0: } michael@0: michael@0: mWidth = backbufferWidth; michael@0: mHeight = backbufferHeight; michael@0: mSwapIntervalDirty = false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height) michael@0: { michael@0: if (!mSwapChain) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: if (x + width > mWidth) michael@0: { michael@0: width = mWidth - x; michael@0: } michael@0: michael@0: if (y + height > mHeight) michael@0: { michael@0: height = mHeight - y; michael@0: } michael@0: michael@0: if (width == 0 || height == 0) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: EGLint status = mSwapChain->swapRect(x, y, width, height); michael@0: michael@0: if (status == EGL_CONTEXT_LOST) michael@0: { michael@0: mRenderer->notifyDeviceLost(); michael@0: return false; michael@0: } michael@0: else if (status != EGL_SUCCESS) michael@0: { michael@0: return error(status, false); michael@0: } michael@0: michael@0: checkForOutOfDateSwapChain(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: HWND Surface::getWindowHandle() michael@0: { michael@0: return mWindow; michael@0: } michael@0: michael@0: michael@0: #define kSurfaceProperty _TEXT("Egl::SurfaceOwner") michael@0: #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc") michael@0: michael@0: static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) michael@0: { michael@0: if (message == WM_SIZE) michael@0: { michael@0: Surface* surf = reinterpret_cast(GetProp(hwnd, kSurfaceProperty)); michael@0: if(surf) michael@0: { michael@0: surf->checkForOutOfDateSwapChain(); michael@0: } michael@0: } michael@0: WNDPROC prevWndFunc = reinterpret_cast(GetProp(hwnd, kParentWndProc)); michael@0: return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam); michael@0: } michael@0: michael@0: void Surface::subclassWindow() michael@0: { michael@0: if (!mWindow) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: DWORD processId; michael@0: DWORD threadId = GetWindowThreadProcessId(mWindow, &processId); michael@0: if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId()) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: SetLastError(0); michael@0: LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast(SurfaceWindowProc)); michael@0: if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS) michael@0: { michael@0: mWindowSubclassed = false; michael@0: return; michael@0: } michael@0: michael@0: SetProp(mWindow, kSurfaceProperty, reinterpret_cast(this)); michael@0: SetProp(mWindow, kParentWndProc, reinterpret_cast(oldWndProc)); michael@0: mWindowSubclassed = true; michael@0: } michael@0: michael@0: void Surface::unsubclassWindow() michael@0: { michael@0: if(!mWindowSubclassed) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: // un-subclass michael@0: LONG_PTR parentWndFunc = reinterpret_cast(GetProp(mWindow, kParentWndProc)); michael@0: michael@0: // Check the windowproc is still SurfaceWindowProc. michael@0: // If this assert fails, then it is likely the application has subclassed the michael@0: // hwnd as well and did not unsubclass before destroying its EGL context. The michael@0: // application should be modified to either subclass before initializing the michael@0: // EGL context, or to unsubclass before destroying the EGL context. michael@0: if(parentWndFunc) michael@0: { michael@0: LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc); michael@0: ASSERT(prevWndFunc == reinterpret_cast(SurfaceWindowProc)); michael@0: } michael@0: michael@0: RemoveProp(mWindow, kSurfaceProperty); michael@0: RemoveProp(mWindow, kParentWndProc); michael@0: mWindowSubclassed = false; michael@0: } michael@0: michael@0: bool Surface::checkForOutOfDateSwapChain() michael@0: { michael@0: RECT client; michael@0: if (!GetClientRect(getWindowHandle(), &client)) michael@0: { michael@0: ASSERT(false); michael@0: return false; michael@0: } michael@0: michael@0: // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information. michael@0: int clientWidth = client.right - client.left; michael@0: int clientHeight = client.bottom - client.top; michael@0: bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight(); michael@0: michael@0: if (mSwapIntervalDirty) michael@0: { michael@0: resetSwapChain(clientWidth, clientHeight); michael@0: } michael@0: else if (sizeDirty) michael@0: { michael@0: resizeSwapChain(clientWidth, clientHeight); michael@0: } michael@0: michael@0: if (mSwapIntervalDirty || sizeDirty) michael@0: { michael@0: if (static_cast(getCurrentDrawSurface()) == this) michael@0: { michael@0: glMakeCurrent(glGetCurrentContext(), static_cast(getCurrentDisplay()), this); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool Surface::swap() michael@0: { michael@0: return swapRect(0, 0, mWidth, mHeight); michael@0: } michael@0: michael@0: bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) michael@0: { michael@0: if (!mPostSubBufferSupported) michael@0: { michael@0: // Spec is not clear about how this should be handled. michael@0: return true; michael@0: } michael@0: michael@0: return swapRect(x, y, width, height); michael@0: } michael@0: michael@0: EGLint Surface::getWidth() const michael@0: { michael@0: return mWidth; michael@0: } michael@0: michael@0: EGLint Surface::getHeight() const michael@0: { michael@0: return mHeight; michael@0: } michael@0: michael@0: EGLint Surface::isPostSubBufferSupported() const michael@0: { michael@0: return mPostSubBufferSupported; michael@0: } michael@0: michael@0: rx::SwapChain *Surface::getSwapChain() const michael@0: { michael@0: return mSwapChain; michael@0: } michael@0: michael@0: void Surface::setSwapInterval(EGLint interval) michael@0: { michael@0: if (mSwapInterval == interval) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: mSwapInterval = interval; michael@0: mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval()); michael@0: mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval()); michael@0: michael@0: mSwapIntervalDirty = true; michael@0: } michael@0: michael@0: EGLenum Surface::getTextureFormat() const michael@0: { michael@0: return mTextureFormat; michael@0: } michael@0: michael@0: EGLenum Surface::getTextureTarget() const michael@0: { michael@0: return mTextureTarget; michael@0: } michael@0: michael@0: void Surface::setBoundTexture(gl::Texture2D *texture) michael@0: { michael@0: mTexture = texture; michael@0: } michael@0: michael@0: gl::Texture2D *Surface::getBoundTexture() const michael@0: { michael@0: return mTexture; michael@0: } michael@0: michael@0: EGLenum Surface::getFormat() const michael@0: { michael@0: return mConfig->mRenderTargetFormat; michael@0: } michael@0: }