blob: 71f5a8179aa0caed3a0fef270866974e4639bd04 [file] [log] [blame]
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001//
daniel@transgaming.com3c720782012-10-31 18:42:34 +00002// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00003// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
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.
10
11#include <tchar.h>
12
13#include "libEGL/Surface.h"
14
15#include "common/debug.h"
16#include "libGLESv2/Texture.h"
daniel@transgaming.com3c720782012-10-31 18:42:34 +000017#include "libGLESv2/renderer/SwapChain.h"
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000018
19#include "libEGL/main.h"
20#include "libEGL/Display.h"
21
22#include <dwmapi.h>
23
24namespace egl
25{
26
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000027Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported)
28 : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported)
29{
daniel@transgaming.combdfb3912012-10-31 19:55:21 +000030 mRenderer = mDisplay->getRenderer9();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000031 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000032 mShareHandle = NULL;
33 mTexture = NULL;
34 mTextureFormat = EGL_NO_TEXTURE;
35 mTextureTarget = EGL_NO_TEXTURE;
36
37 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
38 mRenderBuffer = EGL_BACK_BUFFER;
39 mSwapBehavior = EGL_BUFFER_PRESERVED;
40 mSwapInterval = -1;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000041 mWidth = -1;
42 mHeight = -1;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000043 setSwapInterval(1);
44
45 subclassWindow();
46}
47
48Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
49 : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE)
50{
daniel@transgaming.combdfb3912012-10-31 19:55:21 +000051 mRenderer = mDisplay->getRenderer9();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000052 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000053 mWindowSubclassed = false;
54 mTexture = NULL;
55 mTextureFormat = textureFormat;
56 mTextureTarget = textureType;
57
58 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
59 mRenderBuffer = EGL_BACK_BUFFER;
60 mSwapBehavior = EGL_BUFFER_PRESERVED;
61 mSwapInterval = -1;
62 setSwapInterval(1);
63}
64
65Surface::~Surface()
66{
67 unsubclassWindow();
68 release();
69}
70
71bool Surface::initialize()
72{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000073 if (!resetSwapChain())
74 return false;
75
76 // Modify present parameters for this window, if we are composited,
77 // to minimize the amount of queuing done by DWM between our calls to
78 // present and the actual screen.
79 if (mWindow && (getComparableOSVersion() >= versionWindowsVista)) {
80 BOOL isComposited;
81 HRESULT result = DwmIsCompositionEnabled(&isComposited);
82 if (SUCCEEDED(result) && isComposited) {
83 DWM_PRESENT_PARAMETERS presentParams;
84 memset(&presentParams, 0, sizeof(presentParams));
85 presentParams.cbSize = sizeof(DWM_PRESENT_PARAMETERS);
86 presentParams.cBuffer = 2;
87
88 result = DwmSetPresentParameters(mWindow, &presentParams);
89 if (FAILED(result))
90 ERR("Unable to set present parameters: 0x%08X", result);
91 }
92 }
93
94 return true;
95}
96
97void Surface::release()
98{
daniel@transgaming.com3c720782012-10-31 18:42:34 +000099 glDestroySwapChain(mSwapChain);
100 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000101
102 if (mTexture)
103 {
104 mTexture->releaseTexImage();
105 mTexture = NULL;
106 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000107}
108
109bool Surface::resetSwapChain()
110{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000111 ASSERT(!mSwapChain);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000112
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000113 int width;
114 int height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000115
116 if (mWindow)
117 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000118 RECT windowRect;
119 if (!GetClientRect(getWindowHandle(), &windowRect))
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000120 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000121 ASSERT(false);
122
123 ERR("Could not retrieve the window dimensions");
124 return error(EGL_BAD_SURFACE, false);
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000125 }
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000126
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000127 width = windowRect.right - windowRect.left;
128 height = windowRect.bottom - windowRect.top;
129 }
130 else
131 {
132 // non-window surface - size is determined at creation
133 width = mWidth;
134 height = mHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000135 }
136
daniel@transgaming.com114bd462012-10-31 18:42:47 +0000137 mSwapChain = glCreateSwapChain(mRenderer, mWindow, mShareHandle,
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000138 mConfig->mRenderTargetFormat, mConfig->mDepthStencilFormat);
139 if (!mSwapChain)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000140 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000141 return error(EGL_BAD_ALLOC, false);
142 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000143
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000144 if (!resetSwapChain(width, height))
145 {
146 glDestroySwapChain(mSwapChain);
147 mSwapChain = NULL;
daniel@transgaming.com2a99bfa2012-10-31 19:55:50 +0000148 return false;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000149 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000150
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000151 return true;
152}
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000153
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000154bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
155{
156 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
157 ASSERT(mSwapChain);
158
159 EGLint status = mSwapChain->reset(backbufferWidth, backbufferHeight, mSwapInterval);
160
161 if (status == EGL_CONTEXT_LOST)
162 {
163 mDisplay->notifyDeviceLost();
164 return false;
165 }
166 else if (status != EGL_SUCCESS)
167 {
168 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000169 }
170
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000171 mWidth = backbufferWidth;
172 mHeight = backbufferHeight;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000173 mSwapIntervalDirty = false;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000174
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000175 return true;
176}
177
178bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
179{
180 if (!mSwapChain)
181 {
182 return true;
183 }
184
185 if (x + width > mWidth)
186 {
187 width = mWidth - x;
188 }
189
190 if (y + height > mHeight)
191 {
192 height = mHeight - y;
193 }
194
195 if (width == 0 || height == 0)
196 {
197 return true;
198 }
199
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000200 EGLint status = mSwapChain->swapRect(x, y, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000201
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000202 if (status == EGL_CONTEXT_LOST)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000203 {
204 mDisplay->notifyDeviceLost();
205 return false;
206 }
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000207 else if (status != EGL_SUCCESS)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000208 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000209 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000210 }
211
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000212 checkForOutOfDateSwapChain();
213
214 return true;
215}
216
217HWND Surface::getWindowHandle()
218{
219 return mWindow;
220}
221
222
223#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
224#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
225
226static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
227{
228 if (message == WM_SIZE)
229 {
230 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
231 if(surf)
232 {
233 surf->checkForOutOfDateSwapChain();
234 }
235 }
236 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
237 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
238}
239
240void Surface::subclassWindow()
241{
242 if (!mWindow)
243 {
244 return;
245 }
246
247 DWORD processId;
248 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
249 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
250 {
251 return;
252 }
253
254 SetLastError(0);
255 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
256 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
257 {
258 mWindowSubclassed = false;
259 return;
260 }
261
262 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
263 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
264 mWindowSubclassed = true;
265}
266
267void Surface::unsubclassWindow()
268{
269 if(!mWindowSubclassed)
270 {
271 return;
272 }
273
274 // un-subclass
275 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
276
277 // Check the windowproc is still SurfaceWindowProc.
278 // If this assert fails, then it is likely the application has subclassed the
279 // hwnd as well and did not unsubclass before destroying its EGL context. The
280 // application should be modified to either subclass before initializing the
281 // EGL context, or to unsubclass before destroying the EGL context.
282 if(parentWndFunc)
283 {
284 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
285 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
286 }
287
288 RemoveProp(mWindow, kSurfaceProperty);
289 RemoveProp(mWindow, kParentWndProc);
290 mWindowSubclassed = false;
291}
292
293bool Surface::checkForOutOfDateSwapChain()
294{
295 RECT client;
296 if (!GetClientRect(getWindowHandle(), &client))
297 {
298 ASSERT(false);
299 return false;
300 }
301
302 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
303 int clientWidth = client.right - client.left;
304 int clientHeight = client.bottom - client.top;
305 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
306
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000307 if (sizeDirty || mSwapIntervalDirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000308 {
309 resetSwapChain(clientWidth, clientHeight);
310 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
311 {
312 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
313 }
314
315 return true;
316 }
317 return false;
318}
319
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000320bool Surface::swap()
321{
322 return swapRect(0, 0, mWidth, mHeight);
323}
324
325bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
326{
327 if (!mPostSubBufferSupported)
328 {
329 // Spec is not clear about how this should be handled.
330 return true;
331 }
332
333 return swapRect(x, y, width, height);
334}
335
336EGLint Surface::getWidth() const
337{
338 return mWidth;
339}
340
341EGLint Surface::getHeight() const
342{
343 return mHeight;
344}
345
346EGLint Surface::isPostSubBufferSupported() const
347{
348 return mPostSubBufferSupported;
349}
350
daniel@transgaming.com76d3e6e2012-10-31 19:55:33 +0000351rx::SwapChain *Surface::getSwapChain() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000352{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000353 return mSwapChain;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000354}
355
356void Surface::setSwapInterval(EGLint interval)
357{
358 if (mSwapInterval == interval)
359 {
360 return;
361 }
362
363 mSwapInterval = interval;
daniel@transgaming.com114bd462012-10-31 18:42:47 +0000364 mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval());
365 mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval());
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000366
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000367 mSwapIntervalDirty = true;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000368}
369
370EGLenum Surface::getTextureFormat() const
371{
372 return mTextureFormat;
373}
374
375EGLenum Surface::getTextureTarget() const
376{
377 return mTextureTarget;
378}
379
380void Surface::setBoundTexture(gl::Texture2D *texture)
381{
382 mTexture = texture;
383}
384
385gl::Texture2D *Surface::getBoundTexture() const
386{
387 return mTexture;
388}
389
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000390EGLenum Surface::getFormat() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000391{
392 return mConfig->mRenderTargetFormat;
393}
394}