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