blob: e9a3ac868cb714f5ba8f4ebb4d96958828c972d1 [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{
30 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000031 mShareHandle = NULL;
32 mTexture = NULL;
33 mTextureFormat = EGL_NO_TEXTURE;
34 mTextureTarget = EGL_NO_TEXTURE;
35
36 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
37 mRenderBuffer = EGL_BACK_BUFFER;
38 mSwapBehavior = EGL_BUFFER_PRESERVED;
39 mSwapInterval = -1;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000040 mWidth = -1;
41 mHeight = -1;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000042 setSwapInterval(1);
43
44 subclassWindow();
45}
46
47Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
48 : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE)
49{
50 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000051 mWindowSubclassed = false;
52 mTexture = NULL;
53 mTextureFormat = textureFormat;
54 mTextureTarget = textureType;
55
56 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
57 mRenderBuffer = EGL_BACK_BUFFER;
58 mSwapBehavior = EGL_BUFFER_PRESERVED;
59 mSwapInterval = -1;
60 setSwapInterval(1);
61}
62
63Surface::~Surface()
64{
65 unsubclassWindow();
66 release();
67}
68
69bool Surface::initialize()
70{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000071 if (!resetSwapChain())
72 return false;
73
74 // Modify present parameters for this window, if we are composited,
75 // to minimize the amount of queuing done by DWM between our calls to
76 // present and the actual screen.
77 if (mWindow && (getComparableOSVersion() >= versionWindowsVista)) {
78 BOOL isComposited;
79 HRESULT result = DwmIsCompositionEnabled(&isComposited);
80 if (SUCCEEDED(result) && isComposited) {
81 DWM_PRESENT_PARAMETERS presentParams;
82 memset(&presentParams, 0, sizeof(presentParams));
83 presentParams.cbSize = sizeof(DWM_PRESENT_PARAMETERS);
84 presentParams.cBuffer = 2;
85
86 result = DwmSetPresentParameters(mWindow, &presentParams);
87 if (FAILED(result))
88 ERR("Unable to set present parameters: 0x%08X", result);
89 }
90 }
91
92 return true;
93}
94
95void Surface::release()
96{
daniel@transgaming.com3c720782012-10-31 18:42:34 +000097 glDestroySwapChain(mSwapChain);
98 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000099
100 if (mTexture)
101 {
102 mTexture->releaseTexImage();
103 mTexture = NULL;
104 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000105}
106
107bool Surface::resetSwapChain()
108{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000109 ASSERT(!mSwapChain);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000110
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000111 int width;
112 int height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000113
114 if (mWindow)
115 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000116 RECT windowRect;
117 if (!GetClientRect(getWindowHandle(), &windowRect))
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000118 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000119 ASSERT(false);
120
121 ERR("Could not retrieve the window dimensions");
122 return error(EGL_BAD_SURFACE, false);
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000123 }
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000124
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000125 width = windowRect.right - windowRect.left;
126 height = windowRect.bottom - windowRect.top;
127 }
128 else
129 {
130 // non-window surface - size is determined at creation
131 width = mWidth;
132 height = mHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000133 }
134
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000135 mSwapChain = glCreateSwapChain(mDisplay->getRenderer(), mWindow, mShareHandle,
136 mConfig->mRenderTargetFormat, mConfig->mDepthStencilFormat);
137 if (!mSwapChain)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000138 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000139 return error(EGL_BAD_ALLOC, false);
140 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000141
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000142 if (!resetSwapChain(width, height))
143 {
144 glDestroySwapChain(mSwapChain);
145 mSwapChain = NULL;
146 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000147
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000148 return true;
149}
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000150
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000151bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
152{
153 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
154 ASSERT(mSwapChain);
155
156 EGLint status = mSwapChain->reset(backbufferWidth, backbufferHeight, mSwapInterval);
157
158 if (status == EGL_CONTEXT_LOST)
159 {
160 mDisplay->notifyDeviceLost();
161 return false;
162 }
163 else if (status != EGL_SUCCESS)
164 {
165 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000166 }
167
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000168 mWidth = backbufferWidth;
169 mHeight = backbufferHeight;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000170 mSwapIntervalDirty = false;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000171
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000172 return true;
173}
174
175bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
176{
177 if (!mSwapChain)
178 {
179 return true;
180 }
181
182 if (x + width > mWidth)
183 {
184 width = mWidth - x;
185 }
186
187 if (y + height > mHeight)
188 {
189 height = mHeight - y;
190 }
191
192 if (width == 0 || height == 0)
193 {
194 return true;
195 }
196
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000197 EGLint status = mSwapChain->swapRect(x, y, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000198
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000199 if (status == EGL_CONTEXT_LOST)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000200 {
201 mDisplay->notifyDeviceLost();
202 return false;
203 }
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000204 else if (status != EGL_SUCCESS)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000205 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000206 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000207 }
208
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000209 checkForOutOfDateSwapChain();
210
211 return true;
212}
213
214HWND Surface::getWindowHandle()
215{
216 return mWindow;
217}
218
219
220#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
221#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
222
223static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
224{
225 if (message == WM_SIZE)
226 {
227 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
228 if(surf)
229 {
230 surf->checkForOutOfDateSwapChain();
231 }
232 }
233 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
234 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
235}
236
237void Surface::subclassWindow()
238{
239 if (!mWindow)
240 {
241 return;
242 }
243
244 DWORD processId;
245 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
246 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
247 {
248 return;
249 }
250
251 SetLastError(0);
252 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
253 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
254 {
255 mWindowSubclassed = false;
256 return;
257 }
258
259 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
260 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
261 mWindowSubclassed = true;
262}
263
264void Surface::unsubclassWindow()
265{
266 if(!mWindowSubclassed)
267 {
268 return;
269 }
270
271 // un-subclass
272 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
273
274 // Check the windowproc is still SurfaceWindowProc.
275 // If this assert fails, then it is likely the application has subclassed the
276 // hwnd as well and did not unsubclass before destroying its EGL context. The
277 // application should be modified to either subclass before initializing the
278 // EGL context, or to unsubclass before destroying the EGL context.
279 if(parentWndFunc)
280 {
281 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
282 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
283 }
284
285 RemoveProp(mWindow, kSurfaceProperty);
286 RemoveProp(mWindow, kParentWndProc);
287 mWindowSubclassed = false;
288}
289
290bool Surface::checkForOutOfDateSwapChain()
291{
292 RECT client;
293 if (!GetClientRect(getWindowHandle(), &client))
294 {
295 ASSERT(false);
296 return false;
297 }
298
299 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
300 int clientWidth = client.right - client.left;
301 int clientHeight = client.bottom - client.top;
302 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
303
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000304 if (sizeDirty || mSwapIntervalDirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000305 {
306 resetSwapChain(clientWidth, clientHeight);
307 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
308 {
309 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
310 }
311
312 return true;
313 }
314 return false;
315}
316
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000317bool Surface::swap()
318{
319 return swapRect(0, 0, mWidth, mHeight);
320}
321
322bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
323{
324 if (!mPostSubBufferSupported)
325 {
326 // Spec is not clear about how this should be handled.
327 return true;
328 }
329
330 return swapRect(x, y, width, height);
331}
332
333EGLint Surface::getWidth() const
334{
335 return mWidth;
336}
337
338EGLint Surface::getHeight() const
339{
340 return mHeight;
341}
342
343EGLint Surface::isPostSubBufferSupported() const
344{
345 return mPostSubBufferSupported;
346}
347
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000348renderer::SwapChain *Surface::getSwapChain() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000349{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000350 return mSwapChain;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000351}
352
353void Surface::setSwapInterval(EGLint interval)
354{
355 if (mSwapInterval == interval)
356 {
357 return;
358 }
359
360 mSwapInterval = interval;
361 mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval());
362 mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval());
363
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000364 mSwapIntervalDirty = true;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000365}
366
367EGLenum Surface::getTextureFormat() const
368{
369 return mTextureFormat;
370}
371
372EGLenum Surface::getTextureTarget() const
373{
374 return mTextureTarget;
375}
376
377void Surface::setBoundTexture(gl::Texture2D *texture)
378{
379 mTexture = texture;
380}
381
382gl::Texture2D *Surface::getBoundTexture() const
383{
384 return mTexture;
385}
386
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000387EGLenum Surface::getFormat() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000388{
389 return mConfig->mRenderTargetFormat;
390}
391}