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