blob: 78203b06d07777a1db30e12ae1378f9787f7133e [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.comcfa8efd2012-11-28 19:30:55 +000018#include "libGLESv2/main.h"
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000019
20#include "libEGL/main.h"
21#include "libEGL/Display.h"
22
23#include <dwmapi.h>
24
25namespace egl
26{
27
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000028Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported)
29 : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported)
30{
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +000031 mRenderer = mDisplay->getRenderer();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000032 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000033 mShareHandle = NULL;
34 mTexture = NULL;
35 mTextureFormat = EGL_NO_TEXTURE;
36 mTextureTarget = EGL_NO_TEXTURE;
37
38 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
39 mRenderBuffer = EGL_BACK_BUFFER;
40 mSwapBehavior = EGL_BUFFER_PRESERVED;
41 mSwapInterval = -1;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000042 mWidth = -1;
43 mHeight = -1;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000044 setSwapInterval(1);
45
46 subclassWindow();
47}
48
49Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
50 : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE)
51{
daniel@transgaming.com28e36922012-11-28 21:02:47 +000052 mRenderer = mDisplay->getRenderer();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000053 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000054 mWindowSubclassed = false;
55 mTexture = NULL;
56 mTextureFormat = textureFormat;
57 mTextureTarget = textureType;
58
59 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
60 mRenderBuffer = EGL_BACK_BUFFER;
61 mSwapBehavior = EGL_BUFFER_PRESERVED;
62 mSwapInterval = -1;
63 setSwapInterval(1);
64}
65
66Surface::~Surface()
67{
68 unsubclassWindow();
69 release();
70}
71
72bool Surface::initialize()
73{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000074 if (!resetSwapChain())
75 return false;
76
77 // Modify present parameters for this window, if we are composited,
78 // to minimize the amount of queuing done by DWM between our calls to
79 // present and the actual screen.
80 if (mWindow && (getComparableOSVersion() >= versionWindowsVista)) {
81 BOOL isComposited;
82 HRESULT result = DwmIsCompositionEnabled(&isComposited);
83 if (SUCCEEDED(result) && isComposited) {
84 DWM_PRESENT_PARAMETERS presentParams;
85 memset(&presentParams, 0, sizeof(presentParams));
86 presentParams.cbSize = sizeof(DWM_PRESENT_PARAMETERS);
87 presentParams.cBuffer = 2;
88
89 result = DwmSetPresentParameters(mWindow, &presentParams);
90 if (FAILED(result))
91 ERR("Unable to set present parameters: 0x%08X", result);
92 }
93 }
94
95 return true;
96}
97
98void Surface::release()
99{
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000100 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000101 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000102
103 if (mTexture)
104 {
105 mTexture->releaseTexImage();
106 mTexture = NULL;
107 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000108}
109
110bool Surface::resetSwapChain()
111{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000112 ASSERT(!mSwapChain);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000113
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000114 int width;
115 int height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000116
117 if (mWindow)
118 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000119 RECT windowRect;
120 if (!GetClientRect(getWindowHandle(), &windowRect))
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000121 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000122 ASSERT(false);
123
124 ERR("Could not retrieve the window dimensions");
125 return error(EGL_BAD_SURFACE, false);
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000126 }
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000127
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000128 width = windowRect.right - windowRect.left;
129 height = windowRect.bottom - windowRect.top;
130 }
131 else
132 {
133 // non-window surface - size is determined at creation
134 width = mWidth;
135 height = mHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000136 }
137
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000138 mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle,
139 mConfig->mRenderTargetFormat,
140 mConfig->mDepthStencilFormat);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000141 if (!mSwapChain)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000142 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000143 return error(EGL_BAD_ALLOC, false);
144 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000145
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000146 if (!resetSwapChain(width, height))
147 {
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000148 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000149 mSwapChain = NULL;
daniel@transgaming.com2a99bfa2012-10-31 19:55:50 +0000150 return false;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000151 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000152
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000153 return true;
154}
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000155
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000156bool Surface::resizeSwapChain(int backbufferWidth, int backbufferHeight)
157{
158 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
159 ASSERT(mSwapChain);
160
161 EGLint status = mSwapChain->resize(backbufferWidth, backbufferHeight);
162
163 if (status == EGL_CONTEXT_LOST)
164 {
165 mDisplay->notifyDeviceLost();
166 return false;
167 }
168 else if (status != EGL_SUCCESS)
169 {
170 return error(status, false);
171 }
172
173 mWidth = backbufferWidth;
174 mHeight = backbufferHeight;
175
176 return true;
177}
178
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000179bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
180{
181 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
182 ASSERT(mSwapChain);
183
184 EGLint status = mSwapChain->reset(backbufferWidth, backbufferHeight, mSwapInterval);
185
186 if (status == EGL_CONTEXT_LOST)
187 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000188 mRenderer->notifyDeviceLost();
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000189 return false;
190 }
191 else if (status != EGL_SUCCESS)
192 {
193 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000194 }
195
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000196 mWidth = backbufferWidth;
197 mHeight = backbufferHeight;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000198 mSwapIntervalDirty = false;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000199
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000200 return true;
201}
202
203bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
204{
205 if (!mSwapChain)
206 {
207 return true;
208 }
209
210 if (x + width > mWidth)
211 {
212 width = mWidth - x;
213 }
214
215 if (y + height > mHeight)
216 {
217 height = mHeight - y;
218 }
219
220 if (width == 0 || height == 0)
221 {
222 return true;
223 }
224
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000225 EGLint status = mSwapChain->swapRect(x, y, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000226
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000227 if (status == EGL_CONTEXT_LOST)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000228 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000229 mRenderer->notifyDeviceLost();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000230 return false;
231 }
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000232 else if (status != EGL_SUCCESS)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000233 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000234 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000235 }
236
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000237 checkForOutOfDateSwapChain();
238
239 return true;
240}
241
242HWND Surface::getWindowHandle()
243{
244 return mWindow;
245}
246
247
248#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
249#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
250
251static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
252{
253 if (message == WM_SIZE)
254 {
255 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
256 if(surf)
257 {
258 surf->checkForOutOfDateSwapChain();
259 }
260 }
261 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
262 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
263}
264
265void Surface::subclassWindow()
266{
267 if (!mWindow)
268 {
269 return;
270 }
271
272 DWORD processId;
273 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
274 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
275 {
276 return;
277 }
278
279 SetLastError(0);
280 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
281 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
282 {
283 mWindowSubclassed = false;
284 return;
285 }
286
287 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
288 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
289 mWindowSubclassed = true;
290}
291
292void Surface::unsubclassWindow()
293{
294 if(!mWindowSubclassed)
295 {
296 return;
297 }
298
299 // un-subclass
300 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
301
302 // Check the windowproc is still SurfaceWindowProc.
303 // If this assert fails, then it is likely the application has subclassed the
304 // hwnd as well and did not unsubclass before destroying its EGL context. The
305 // application should be modified to either subclass before initializing the
306 // EGL context, or to unsubclass before destroying the EGL context.
307 if(parentWndFunc)
308 {
309 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
310 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
311 }
312
313 RemoveProp(mWindow, kSurfaceProperty);
314 RemoveProp(mWindow, kParentWndProc);
315 mWindowSubclassed = false;
316}
317
318bool Surface::checkForOutOfDateSwapChain()
319{
320 RECT client;
321 if (!GetClientRect(getWindowHandle(), &client))
322 {
323 ASSERT(false);
324 return false;
325 }
326
327 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
328 int clientWidth = client.right - client.left;
329 int clientHeight = client.bottom - client.top;
330 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
331
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000332 if (mSwapIntervalDirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000333 {
334 resetSwapChain(clientWidth, clientHeight);
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000335 }
336 else if (sizeDirty)
337 {
338 resizeSwapChain(clientWidth, clientHeight);
339 }
340
341 if (mSwapIntervalDirty || sizeDirty)
342 {
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000343 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
344 {
345 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
346 }
347
348 return true;
349 }
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000350
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000351 return false;
352}
353
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000354bool Surface::swap()
355{
356 return swapRect(0, 0, mWidth, mHeight);
357}
358
359bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
360{
361 if (!mPostSubBufferSupported)
362 {
363 // Spec is not clear about how this should be handled.
364 return true;
365 }
366
367 return swapRect(x, y, width, height);
368}
369
370EGLint Surface::getWidth() const
371{
372 return mWidth;
373}
374
375EGLint Surface::getHeight() const
376{
377 return mHeight;
378}
379
380EGLint Surface::isPostSubBufferSupported() const
381{
382 return mPostSubBufferSupported;
383}
384
daniel@transgaming.com76d3e6e2012-10-31 19:55:33 +0000385rx::SwapChain *Surface::getSwapChain() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000386{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000387 return mSwapChain;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000388}
389
390void Surface::setSwapInterval(EGLint interval)
391{
392 if (mSwapInterval == interval)
393 {
394 return;
395 }
396
397 mSwapInterval = interval;
daniel@transgaming.com114bd462012-10-31 18:42:47 +0000398 mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval());
399 mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval());
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000400
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000401 mSwapIntervalDirty = true;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000402}
403
404EGLenum Surface::getTextureFormat() const
405{
406 return mTextureFormat;
407}
408
409EGLenum Surface::getTextureTarget() const
410{
411 return mTextureTarget;
412}
413
414void Surface::setBoundTexture(gl::Texture2D *texture)
415{
416 mTexture = texture;
417}
418
419gl::Texture2D *Surface::getBoundTexture() const
420{
421 return mTexture;
422}
423
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000424EGLenum Surface::getFormat() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000425{
426 return mConfig->mRenderTargetFormat;
427}
428}