blob: 12f8dfdf25024b21732c38d1d9cbd4c64f626c9d [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
Scott Graham86f601c2013-09-17 13:28:00 -070013#include <algorithm>
14
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000015#include "libEGL/Surface.h"
16
17#include "common/debug.h"
18#include "libGLESv2/Texture.h"
daniel@transgaming.com3c720782012-10-31 18:42:34 +000019#include "libGLESv2/renderer/SwapChain.h"
daniel@transgaming.comcfa8efd2012-11-28 19:30:55 +000020#include "libGLESv2/main.h"
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000021
22#include "libEGL/main.h"
23#include "libEGL/Display.h"
24
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000025namespace 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
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000077 return true;
78}
79
80void Surface::release()
81{
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +000082 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000083 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000084
85 if (mTexture)
86 {
87 mTexture->releaseTexImage();
88 mTexture = NULL;
89 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000090}
91
92bool Surface::resetSwapChain()
93{
daniel@transgaming.com3c720782012-10-31 18:42:34 +000094 ASSERT(!mSwapChain);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000095
daniel@transgaming.com3c720782012-10-31 18:42:34 +000096 int width;
97 int height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000098
99 if (mWindow)
100 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000101 RECT windowRect;
102 if (!GetClientRect(getWindowHandle(), &windowRect))
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000103 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000104 ASSERT(false);
105
106 ERR("Could not retrieve the window dimensions");
107 return error(EGL_BAD_SURFACE, false);
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000108 }
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000109
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000110 width = windowRect.right - windowRect.left;
111 height = windowRect.bottom - windowRect.top;
112 }
113 else
114 {
115 // non-window surface - size is determined at creation
116 width = mWidth;
117 height = mHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000118 }
119
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000120 mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle,
121 mConfig->mRenderTargetFormat,
122 mConfig->mDepthStencilFormat);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000123 if (!mSwapChain)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000124 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000125 return error(EGL_BAD_ALLOC, false);
126 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000127
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000128 if (!resetSwapChain(width, height))
129 {
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000130 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000131 mSwapChain = NULL;
daniel@transgaming.com2a99bfa2012-10-31 19:55:50 +0000132 return false;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000133 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000134
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000135 return true;
136}
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000137
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000138bool Surface::resizeSwapChain(int backbufferWidth, int backbufferHeight)
139{
140 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
141 ASSERT(mSwapChain);
142
143 EGLint status = mSwapChain->resize(backbufferWidth, backbufferHeight);
144
145 if (status == EGL_CONTEXT_LOST)
146 {
147 mDisplay->notifyDeviceLost();
148 return false;
149 }
150 else if (status != EGL_SUCCESS)
151 {
152 return error(status, false);
153 }
154
155 mWidth = backbufferWidth;
156 mHeight = backbufferHeight;
157
158 return true;
159}
160
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000161bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
162{
163 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
164 ASSERT(mSwapChain);
165
166 EGLint status = mSwapChain->reset(backbufferWidth, backbufferHeight, mSwapInterval);
167
168 if (status == EGL_CONTEXT_LOST)
169 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000170 mRenderer->notifyDeviceLost();
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000171 return false;
172 }
173 else if (status != EGL_SUCCESS)
174 {
175 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000176 }
177
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000178 mWidth = backbufferWidth;
179 mHeight = backbufferHeight;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000180 mSwapIntervalDirty = false;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000181
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000182 return true;
183}
184
185bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
186{
187 if (!mSwapChain)
188 {
189 return true;
190 }
191
192 if (x + width > mWidth)
193 {
194 width = mWidth - x;
195 }
196
197 if (y + height > mHeight)
198 {
199 height = mHeight - y;
200 }
201
202 if (width == 0 || height == 0)
203 {
204 return true;
205 }
206
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000207 EGLint status = mSwapChain->swapRect(x, y, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000208
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000209 if (status == EGL_CONTEXT_LOST)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000210 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000211 mRenderer->notifyDeviceLost();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000212 return false;
213 }
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000214 else if (status != EGL_SUCCESS)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000215 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000216 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000217 }
218
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000219 checkForOutOfDateSwapChain();
220
221 return true;
222}
223
224HWND Surface::getWindowHandle()
225{
226 return mWindow;
227}
228
229
230#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
231#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
232
233static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
234{
235 if (message == WM_SIZE)
236 {
237 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
238 if(surf)
239 {
240 surf->checkForOutOfDateSwapChain();
241 }
242 }
243 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
244 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
245}
246
247void Surface::subclassWindow()
248{
249 if (!mWindow)
250 {
251 return;
252 }
253
254 DWORD processId;
255 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
256 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
257 {
258 return;
259 }
260
261 SetLastError(0);
262 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
263 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
264 {
265 mWindowSubclassed = false;
266 return;
267 }
268
269 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
270 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
271 mWindowSubclassed = true;
272}
273
274void Surface::unsubclassWindow()
275{
276 if(!mWindowSubclassed)
277 {
278 return;
279 }
280
281 // un-subclass
282 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
283
284 // Check the windowproc is still SurfaceWindowProc.
285 // If this assert fails, then it is likely the application has subclassed the
286 // hwnd as well and did not unsubclass before destroying its EGL context. The
287 // application should be modified to either subclass before initializing the
288 // EGL context, or to unsubclass before destroying the EGL context.
289 if(parentWndFunc)
290 {
291 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
292 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
293 }
294
295 RemoveProp(mWindow, kSurfaceProperty);
296 RemoveProp(mWindow, kParentWndProc);
297 mWindowSubclassed = false;
298}
299
300bool Surface::checkForOutOfDateSwapChain()
301{
302 RECT client;
303 if (!GetClientRect(getWindowHandle(), &client))
304 {
305 ASSERT(false);
306 return false;
307 }
308
309 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
310 int clientWidth = client.right - client.left;
311 int clientHeight = client.bottom - client.top;
312 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
313
John Bauman827a4712013-10-29 16:03:11 -0700314 if (IsIconic(getWindowHandle()))
315 {
316 // The window is automatically resized to 150x22 when it's minimized, but the swapchain shouldn't be resized
317 // because that's not a useful size to render to.
318 sizeDirty = false;
319 }
320
Jamie Madill58e60322013-12-02 11:09:36 -0500321 bool wasDirty = (mSwapIntervalDirty || sizeDirty);
322
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000323 if (mSwapIntervalDirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000324 {
325 resetSwapChain(clientWidth, clientHeight);
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000326 }
327 else if (sizeDirty)
328 {
329 resizeSwapChain(clientWidth, clientHeight);
330 }
331
Jamie Madill58e60322013-12-02 11:09:36 -0500332 if (wasDirty)
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000333 {
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000334 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
335 {
336 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
337 }
338
339 return true;
340 }
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000341
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000342 return false;
343}
344
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000345bool Surface::swap()
346{
347 return swapRect(0, 0, mWidth, mHeight);
348}
349
350bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
351{
352 if (!mPostSubBufferSupported)
353 {
354 // Spec is not clear about how this should be handled.
355 return true;
356 }
357
358 return swapRect(x, y, width, height);
359}
360
361EGLint Surface::getWidth() const
362{
363 return mWidth;
364}
365
366EGLint Surface::getHeight() const
367{
368 return mHeight;
369}
370
371EGLint Surface::isPostSubBufferSupported() const
372{
373 return mPostSubBufferSupported;
374}
375
daniel@transgaming.com76d3e6e2012-10-31 19:55:33 +0000376rx::SwapChain *Surface::getSwapChain() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000377{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000378 return mSwapChain;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000379}
380
381void Surface::setSwapInterval(EGLint interval)
382{
383 if (mSwapInterval == interval)
384 {
385 return;
386 }
387
388 mSwapInterval = interval;
daniel@transgaming.com114bd462012-10-31 18:42:47 +0000389 mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval());
390 mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval());
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000391
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000392 mSwapIntervalDirty = true;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000393}
394
395EGLenum Surface::getTextureFormat() const
396{
397 return mTextureFormat;
398}
399
400EGLenum Surface::getTextureTarget() const
401{
402 return mTextureTarget;
403}
404
405void Surface::setBoundTexture(gl::Texture2D *texture)
406{
407 mTexture = texture;
408}
409
410gl::Texture2D *Surface::getBoundTexture() const
411{
412 return mTexture;
413}
414
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000415EGLenum Surface::getFormat() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000416{
417 return mConfig->mRenderTargetFormat;
418}
419}