blob: 251632d2c257820698eccbb0c073d58752bb7b36 [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;
148 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000149
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000150 return true;
151}
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000152
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000153bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
154{
155 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
156 ASSERT(mSwapChain);
157
158 EGLint status = mSwapChain->reset(backbufferWidth, backbufferHeight, mSwapInterval);
159
160 if (status == EGL_CONTEXT_LOST)
161 {
162 mDisplay->notifyDeviceLost();
163 return false;
164 }
165 else if (status != EGL_SUCCESS)
166 {
167 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000168 }
169
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000170 mWidth = backbufferWidth;
171 mHeight = backbufferHeight;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000172 mSwapIntervalDirty = false;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000173
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000174 return true;
175}
176
177bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
178{
179 if (!mSwapChain)
180 {
181 return true;
182 }
183
184 if (x + width > mWidth)
185 {
186 width = mWidth - x;
187 }
188
189 if (y + height > mHeight)
190 {
191 height = mHeight - y;
192 }
193
194 if (width == 0 || height == 0)
195 {
196 return true;
197 }
198
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000199 EGLint status = mSwapChain->swapRect(x, y, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000200
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000201 if (status == EGL_CONTEXT_LOST)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000202 {
203 mDisplay->notifyDeviceLost();
204 return false;
205 }
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000206 else if (status != EGL_SUCCESS)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000207 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000208 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000209 }
210
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000211 checkForOutOfDateSwapChain();
212
213 return true;
214}
215
216HWND Surface::getWindowHandle()
217{
218 return mWindow;
219}
220
221
222#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
223#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
224
225static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
226{
227 if (message == WM_SIZE)
228 {
229 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
230 if(surf)
231 {
232 surf->checkForOutOfDateSwapChain();
233 }
234 }
235 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
236 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
237}
238
239void Surface::subclassWindow()
240{
241 if (!mWindow)
242 {
243 return;
244 }
245
246 DWORD processId;
247 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
248 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
249 {
250 return;
251 }
252
253 SetLastError(0);
254 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
255 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
256 {
257 mWindowSubclassed = false;
258 return;
259 }
260
261 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
262 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
263 mWindowSubclassed = true;
264}
265
266void Surface::unsubclassWindow()
267{
268 if(!mWindowSubclassed)
269 {
270 return;
271 }
272
273 // un-subclass
274 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
275
276 // Check the windowproc is still SurfaceWindowProc.
277 // If this assert fails, then it is likely the application has subclassed the
278 // hwnd as well and did not unsubclass before destroying its EGL context. The
279 // application should be modified to either subclass before initializing the
280 // EGL context, or to unsubclass before destroying the EGL context.
281 if(parentWndFunc)
282 {
283 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
284 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
285 }
286
287 RemoveProp(mWindow, kSurfaceProperty);
288 RemoveProp(mWindow, kParentWndProc);
289 mWindowSubclassed = false;
290}
291
292bool Surface::checkForOutOfDateSwapChain()
293{
294 RECT client;
295 if (!GetClientRect(getWindowHandle(), &client))
296 {
297 ASSERT(false);
298 return false;
299 }
300
301 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
302 int clientWidth = client.right - client.left;
303 int clientHeight = client.bottom - client.top;
304 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
305
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000306 if (sizeDirty || mSwapIntervalDirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000307 {
308 resetSwapChain(clientWidth, clientHeight);
309 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
310 {
311 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
312 }
313
314 return true;
315 }
316 return false;
317}
318
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000319bool Surface::swap()
320{
321 return swapRect(0, 0, mWidth, mHeight);
322}
323
324bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
325{
326 if (!mPostSubBufferSupported)
327 {
328 // Spec is not clear about how this should be handled.
329 return true;
330 }
331
332 return swapRect(x, y, width, height);
333}
334
335EGLint Surface::getWidth() const
336{
337 return mWidth;
338}
339
340EGLint Surface::getHeight() const
341{
342 return mHeight;
343}
344
345EGLint Surface::isPostSubBufferSupported() const
346{
347 return mPostSubBufferSupported;
348}
349
daniel@transgaming.com76d3e6e2012-10-31 19:55:33 +0000350rx::SwapChain *Surface::getSwapChain() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000351{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000352 return mSwapChain;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000353}
354
355void Surface::setSwapInterval(EGLint interval)
356{
357 if (mSwapInterval == interval)
358 {
359 return;
360 }
361
362 mSwapInterval = interval;
daniel@transgaming.com114bd462012-10-31 18:42:47 +0000363 mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval());
364 mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval());
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000365
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000366 mSwapIntervalDirty = true;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000367}
368
369EGLenum Surface::getTextureFormat() const
370{
371 return mTextureFormat;
372}
373
374EGLenum Surface::getTextureTarget() const
375{
376 return mTextureTarget;
377}
378
379void Surface::setBoundTexture(gl::Texture2D *texture)
380{
381 mTexture = texture;
382}
383
384gl::Texture2D *Surface::getBoundTexture() const
385{
386 return mTexture;
387}
388
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000389EGLenum Surface::getFormat() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000390{
391 return mConfig->mRenderTargetFormat;
392}
393}