Resize surface on receipt of WM_SIZE to avoid corruption during resize. We hook WM_SIZE using window subclassing.
This is a continuation of http://codereview.appspot.com/3038042/
Review URL: http://codereview.appspot.com/3122041
git-svn-id: http://angleproject.googlecode.com/svn/trunk@486 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/samples/gles2_book/Common/Win32/esUtil_win32.c b/samples/gles2_book/Common/Win32/esUtil_win32.c
index 01ef1ce..c27131e 100644
--- a/samples/gles2_book/Common/Win32/esUtil_win32.c
+++ b/samples/gles2_book/Common/Win32/esUtil_win32.c
@@ -40,6 +40,16 @@
case WM_CREATE:
break;
+ case WM_SIZE:
+ {
+ ESContext *esContext = (ESContext*)(LONG_PTR) GetWindowLongPtr ( hWnd, GWL_USERDATA );
+ if ( esContext ) {
+ esContext->width = LOWORD( lParam );
+ esContext->height = HIWORD( lParam );
+ InvalidateRect( esContext->hWnd, NULL, FALSE );
+ }
+ }
+
case WM_PAINT:
{
ESContext *esContext = (ESContext*)(LONG_PTR) GetWindowLongPtr ( hWnd, GWL_USERDATA );
@@ -47,7 +57,8 @@
if ( esContext && esContext->drawFunc )
esContext->drawFunc ( esContext );
- ValidateRect( esContext->hWnd, NULL );
+ if ( esContext )
+ ValidateRect( esContext->hWnd, NULL );
}
break;
@@ -103,7 +114,7 @@
if (!RegisterClass (&wndclass) )
return FALSE;
- wStyle = WS_VISIBLE | WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION;
+ wStyle = WS_VISIBLE | WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION | WS_SIZEBOX;
// Adjust the window rectangle so that the client area has
// the correct number of pixels
diff --git a/src/libEGL/Surface.cpp b/src/libEGL/Surface.cpp
index 3b5add4..8b54ccc 100644
--- a/src/libEGL/Surface.cpp
+++ b/src/libEGL/Surface.cpp
@@ -34,11 +34,13 @@
mSwapInterval = -1;
setSwapInterval(1);
+ subclassWindow();
resetSwapChain();
}
Surface::~Surface()
{
+ unsubclassWindow();
release();
}
@@ -89,6 +91,18 @@
void Surface::resetSwapChain()
{
+ RECT windowRect;
+ if (!GetClientRect(getWindowHandle(), &windowRect))
+ {
+ ASSERT(false);
+ return;
+ }
+
+ resetSwapChain(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
+}
+
+void Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
+{
IDirect3DDevice9 *device = mDisplay->getDevice();
if (device == NULL)
@@ -109,16 +123,8 @@
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;
+ presentParameters.BackBufferWidth = backbufferWidth;
+ presentParameters.BackBufferHeight = backbufferHeight;
IDirect3DSwapChain9 *swapChain = NULL;
HRESULT result = device->CreateAdditionalSwapChain(&presentParameters, &swapChain);
@@ -199,6 +205,8 @@
mPresentIntervalDirty = false;
+ InvalidateRect(mWindow, NULL, FALSE);
+
// The flip state block recorded mFlipTexture so it is now invalid.
releaseRecordedState(device);
}
@@ -336,6 +344,52 @@
mPreFlipState = NULL;
}
}
+#define kSurfaceProperty L"Egl::SurfaceOwner"
+#define kParentWndProc L"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()
+{
+ SetLastError(0);
+ LONG oldWndProc = SetWindowLong(mWindow, GWL_WNDPROC, reinterpret_cast<LONG>(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;
+ // 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.
+ ASSERT(GetWindowLong(mWindow, GWL_WNDPROC) == reinterpret_cast<LONG>(SurfaceWindowProc));
+
+ // un-subclass
+ LONG prevWndFunc = reinterpret_cast<LONG>(GetProp(mWindow, kParentWndProc));
+ SetWindowLong(mWindow, GWL_WNDPROC, prevWndFunc);
+ RemoveProp(mWindow, kSurfaceProperty);
+ RemoveProp(mWindow, kParentWndProc);
+ mWindowSubclassed = false;
+}
bool Surface::checkForOutOfDateSwapChain()
{
@@ -346,10 +400,14 @@
return false;
}
- if (getWidth() != client.right - client.left || getHeight() != client.bottom - client.top || mPresentIntervalDirty)
- {
- resetSwapChain();
+ // 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 (sizeDirty || mPresentIntervalDirty)
+ {
+ resetSwapChain(clientWidth, clientHeight);
if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
{
glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
@@ -357,7 +415,6 @@
return true;
}
-
return false;
}
@@ -387,11 +444,6 @@
IDirect3DSurface9 *renderTarget = mRenderTarget;
renderTarget->AddRef();
- EGLint oldWidth = mWidth;
- EGLint oldHeight = mHeight;
-
- checkForOutOfDateSwapChain();
-
IDirect3DDevice9 *device = mDisplay->getDevice();
IDirect3DSurface9 *textureSurface;
@@ -404,25 +456,26 @@
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
+ // Texcoords are chosen to flip the renderTarget about its Y axis.
+ float w = static_cast<float>(getWidth());
+ float h = static_cast<float>(getHeight());
+ float quad[4][6] = {{0 - 0.5f, 0 - 0.5f, 0.0f, 1.0f, 0.0f, 1.0f},
+ {w - 0.5f, 0 - 0.5f, 0.0f, 1.0f, 1.0f, 1.0f},
+ {w - 0.5f, h - 0.5f, 0.0f, 1.0f, 1.0f, 0.0f},
+ {0 - 0.5f, h - 0.5f, 0.0f, 1.0f, 0.0f, 0.0f}}; // 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)
@@ -437,6 +490,7 @@
ASSERT(SUCCEEDED(result));
+ checkForOutOfDateSwapChain();
}
return true;
diff --git a/src/libEGL/Surface.h b/src/libEGL/Surface.h
index 06bbca1..c9cecb3 100644
--- a/src/libEGL/Surface.h
+++ b/src/libEGL/Surface.h
@@ -42,8 +42,8 @@
virtual IDirect3DSurface9 *getDepthStencil();
void setSwapInterval(EGLint interval);
-
- private:
+ bool checkForOutOfDateSwapChain(); // returns true if swapchain changed due to resize or interval update
+private:
DISALLOW_COPY_AND_ASSIGN(Surface);
Display *const mDisplay;
@@ -53,8 +53,9 @@
IDirect3DSurface9 *mDepthStencil;
IDirect3DTexture9 *mFlipTexture;
- bool checkForOutOfDateSwapChain();
-
+ void subclassWindow();
+ void unsubclassWindow();
+ void resetSwapChain(int backbufferWidth, int backbufferHeight);
static DWORD convertInterval(EGLint interval);
void applyFlipState(IDirect3DDevice9 *device);
@@ -67,6 +68,7 @@
IDirect3DSurface9 *mPreFlipDepthStencil;
const HWND mWindow; // Window that the surface is created for.
+ bool mWindowSubclassed; // Indicates whether we successfully subclassed mWindow for WM_RESIZE hooking
const egl::Config *mConfig; // EGL config surface was created with
EGLint mHeight; // Height of surface
EGLint mWidth; // Width of surface