summaryrefslogtreecommitdiff
path: root/src/libEGL/Surface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libEGL/Surface.cpp')
-rw-r--r--src/libEGL/Surface.cpp408
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;
+}
+}