2 // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
7 // Surface.cpp: Implements the egl::Surface class, representing a drawing surface
8 // such as the client area of a window, including any back buffers.
9 // Implements EGLSurface and related functionality. [EGL 1.4] section 2.2 page 3.
13 #include "libEGL/Surface.h"
15 #include "common/debug.h"
16 #include "libGLESv2/Texture.h"
18 #include "libEGL/main.h"
19 #include "libEGL/Display.h"
28 const int versionWindowsVista = MAKEWORD(0x00, 0x06);
29 const int versionWindows7 = MAKEWORD(0x01, 0x06);
31 // Return the version of the operating system in a format suitable for ordering
33 int getComparableOSVersion()
35 DWORD version = GetVersion();
36 int majorVersion = LOBYTE(LOWORD(version));
37 int minorVersion = HIBYTE(LOWORD(version));
38 return MAKEWORD(minorVersion, majorVersion);
42 Surface::Surface(Display *display, const Config *config, HWND window)
43 : mDisplay(display), mConfig(config), mWindow(window)
48 mOffscreenTexture = NULL;
51 mTextureFormat = EGL_NO_TEXTURE;
52 mTextureTarget = EGL_NO_TEXTURE;
54 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
55 mRenderBuffer = EGL_BACK_BUFFER;
56 mSwapBehavior = EGL_BUFFER_PRESERVED;
63 Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
64 : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height)
69 mOffscreenTexture = NULL;
70 mWindowSubclassed = false;
72 mTextureFormat = textureFormat;
73 mTextureTarget = textureType;
75 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
76 mRenderBuffer = EGL_BACK_BUFFER;
77 mSwapBehavior = EGL_BUFFER_PRESERVED;
88 bool Surface::initialize()
90 ASSERT(!mSwapChain && !mOffscreenTexture && !mDepthStencil);
92 if (!resetSwapChain())
95 // Modify present parameters for this window, if we are composited,
96 // to minimize the amount of queuing done by DWM between our calls to
97 // present and the actual screen.
98 if (mWindow && (getComparableOSVersion() >= versionWindowsVista)) {
100 HRESULT result = DwmIsCompositionEnabled(&isComposited);
101 if (SUCCEEDED(result) && isComposited) {
102 DWM_PRESENT_PARAMETERS presentParams;
103 memset(&presentParams, 0, sizeof(presentParams));
104 presentParams.cbSize = sizeof(DWM_PRESENT_PARAMETERS);
105 presentParams.cBuffer = 2;
107 result = DwmSetPresentParameters(mWindow, &presentParams);
109 ERR("Unable to set present parameters: %081X", result);
116 void Surface::release()
120 mSwapChain->Release();
126 mDepthStencil->Release();
127 mDepthStencil = NULL;
132 mRenderTarget->Release();
133 mRenderTarget = NULL;
136 if (mOffscreenTexture)
138 mOffscreenTexture->Release();
139 mOffscreenTexture = NULL;
144 mTexture->releaseTexImage();
149 bool Surface::resetSwapChain()
153 return resetSwapChain(mWidth, mHeight);
157 if (!GetClientRect(getWindowHandle(), &windowRect))
161 ERR("Could not retrieve the window dimensions");
165 return resetSwapChain(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
168 bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
170 IDirect3DDevice9 *device = mDisplay->getDevice();
177 // Evict all non-render target textures to system memory and release all resources
178 // before reallocating them to free up as much video memory as possible.
179 device->EvictManagedResources();
182 D3DPRESENT_PARAMETERS presentParameters = {0};
185 bool useFlipEx = (getComparableOSVersion() >= versionWindows7) && mDisplay->isD3d9ExDevice();
187 // FlipEx causes unseemly stretching when resizing windows AND when one
188 // draws outside of the WM_PAINT callback. While this is seldom a problem in
189 // single process applications, it is particuarly noticeable in multiprocess
190 // applications. Therefore, if the creator process of our window is not in
191 // the current process, disable use of FlipEx.
193 GetWindowThreadProcessId(mWindow, &windowPID);
194 if(windowPID != GetCurrentProcessId())
197 presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat;
198 // We set BackBufferCount = 1 even when we use D3DSWAPEFFECT_FLIPEX.
199 // We do this because DirectX docs are a bit vague whether to set this to 1
200 // or 2. The runtime seems to accept 1, so we speculate that either it is
201 // forcing it to 2 without telling us, or better, doing something smart
202 // behind the scenes knowing that we don't need more.
203 presentParameters.BackBufferCount = 1;
204 presentParameters.BackBufferFormat = mConfig->mRenderTargetFormat;
205 presentParameters.EnableAutoDepthStencil = FALSE;
206 presentParameters.Flags = 0;
207 presentParameters.hDeviceWindow = getWindowHandle();
208 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented
209 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented
210 presentParameters.PresentationInterval = mPresentInterval;
211 // Use flipEx on Win7 or greater.
213 presentParameters.SwapEffect = D3DSWAPEFFECT_FLIPEX;
215 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
216 presentParameters.Windowed = TRUE;
217 presentParameters.BackBufferWidth = backbufferWidth;
218 presentParameters.BackBufferHeight = backbufferHeight;
222 result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
224 HANDLE *pShareHandle = NULL;
225 if (mDisplay->isD3d9ExDevice()) {
226 pShareHandle = &mShareHandle;
229 result = device->CreateTexture(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 1, D3DUSAGE_RENDERTARGET,
230 presentParameters.BackBufferFormat, D3DPOOL_DEFAULT, &mOffscreenTexture, pShareHandle);
235 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
237 ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
239 return error(EGL_BAD_ALLOC, false);
242 if (mConfig->mDepthStencilFormat != D3DFMT_UNKNOWN)
244 result = device->CreateDepthStencilSurface(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight,
245 presentParameters.AutoDepthStencilFormat, presentParameters.MultiSampleType,
246 presentParameters.MultiSampleQuality, FALSE, &mDepthStencil, NULL);
251 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
253 ERR("Could not create depthstencil surface for new swap chain: %08lX", result);
255 return error(EGL_BAD_ALLOC, false);
259 mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mRenderTarget);
260 InvalidateRect(mWindow, NULL, FALSE);
262 mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
265 mWidth = presentParameters.BackBufferWidth;
266 mHeight = presentParameters.BackBufferHeight;
268 mPresentIntervalDirty = false;
272 HWND Surface::getWindowHandle()
278 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
279 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
281 static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
283 if (message == WM_SIZE)
285 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
288 surf->checkForOutOfDateSwapChain();
291 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
292 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
295 void Surface::subclassWindow()
303 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
304 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
310 LONG oldWndProc = SetWindowLong(mWindow, GWL_WNDPROC, reinterpret_cast<LONG>(SurfaceWindowProc));
311 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
313 mWindowSubclassed = false;
317 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
318 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
319 mWindowSubclassed = true;
322 void Surface::unsubclassWindow()
324 if(!mWindowSubclassed)
330 LONG parentWndFunc = reinterpret_cast<LONG>(GetProp(mWindow, kParentWndProc));
332 // Check the windowproc is still SurfaceWindowProc.
333 // If this assert fails, then it is likely the application has subclassed the
334 // hwnd as well and did not unsubclass before destroying its EGL context. The
335 // application should be modified to either subclass before initializing the
336 // EGL context, or to unsubclass before destroying the EGL context.
339 LONG prevWndFunc = SetWindowLong(mWindow, GWL_WNDPROC, parentWndFunc);
340 ASSERT(prevWndFunc == reinterpret_cast<LONG>(SurfaceWindowProc));
343 RemoveProp(mWindow, kSurfaceProperty);
344 RemoveProp(mWindow, kParentWndProc);
345 mWindowSubclassed = false;
348 bool Surface::checkForOutOfDateSwapChain()
351 if (!GetClientRect(getWindowHandle(), &client))
357 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
358 int clientWidth = client.right - client.left;
359 int clientHeight = client.bottom - client.top;
360 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
362 if (sizeDirty || mPresentIntervalDirty)
364 resetSwapChain(clientWidth, clientHeight);
365 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
367 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
375 DWORD Surface::convertInterval(EGLint interval)
379 case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
380 case 1: return D3DPRESENT_INTERVAL_ONE;
381 case 2: return D3DPRESENT_INTERVAL_TWO;
382 case 3: return D3DPRESENT_INTERVAL_THREE;
383 case 4: return D3DPRESENT_INTERVAL_FOUR;
384 default: UNREACHABLE();
387 return D3DPRESENT_INTERVAL_DEFAULT;
394 mDisplay->endScene();
396 HRESULT result = mSwapChain->Present(NULL, NULL, NULL, NULL, 0);
398 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
400 return error(EGL_BAD_ALLOC, false);
403 if (result == D3DERR_DEVICELOST || result == D3DERR_DEVICEHUNG || result == D3DERR_DEVICEREMOVED)
405 return error(EGL_CONTEXT_LOST, false);
408 ASSERT(SUCCEEDED(result));
410 checkForOutOfDateSwapChain();
416 EGLint Surface::getWidth() const
421 EGLint Surface::getHeight() const
426 IDirect3DSurface9 *Surface::getRenderTarget()
430 mRenderTarget->AddRef();
433 return mRenderTarget;
436 IDirect3DSurface9 *Surface::getDepthStencil()
440 mDepthStencil->AddRef();
443 return mDepthStencil;
446 IDirect3DTexture9 *Surface::getOffscreenTexture()
448 if (mOffscreenTexture)
450 mOffscreenTexture->AddRef();
453 return mOffscreenTexture;
456 void Surface::setSwapInterval(EGLint interval)
458 if (mSwapInterval == interval)
463 mSwapInterval = interval;
464 mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval());
465 mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval());
467 mPresentInterval = convertInterval(mSwapInterval);
468 mPresentIntervalDirty = true;
471 EGLenum Surface::getTextureFormat() const
473 return mTextureFormat;
476 EGLenum Surface::getTextureTarget() const
478 return mTextureTarget;
481 void Surface::setBoundTexture(gl::Texture2D *texture)
486 gl::Texture2D *Surface::getBoundTexture() const
491 D3DFORMAT Surface::getFormat() const
493 return mConfig->mRenderTargetFormat;