blob: 30f85cda9966aa51920040e376bcb95db77c1cc2 [file] [log] [blame]
//
// 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 <algorithm>
#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;
}
}