blob: 13b0f208a317731fea557a504d90fe988c6a7e19 [file] [log] [blame]
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001//
Nicolas Capens3501c162014-05-21 13:27:15 -04002// Copyright (c) 2002-2014 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
John Bauman3dc300a2014-01-28 15:30:35 -080028Surface::Surface(Display *display, const Config *config, HWND window, EGLint fixedSize, EGLint width, EGLint height, EGLint postSubBufferSupported)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000029 : 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;
John Bauman3dc300a2014-01-28 15:30:35 -080042 mWidth = width;
43 mHeight = height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000044 setSwapInterval(1);
John Bauman3dc300a2014-01-28 15:30:35 -080045 mFixedSize = fixedSize;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000046
47 subclassWindow();
48}
49
50Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
51 : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE)
52{
daniel@transgaming.com28e36922012-11-28 21:02:47 +000053 mRenderer = mDisplay->getRenderer();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000054 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000055 mWindowSubclassed = false;
56 mTexture = NULL;
57 mTextureFormat = textureFormat;
58 mTextureTarget = textureType;
59
60 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
61 mRenderBuffer = EGL_BACK_BUFFER;
62 mSwapBehavior = EGL_BUFFER_PRESERVED;
63 mSwapInterval = -1;
64 setSwapInterval(1);
John Bauman3dc300a2014-01-28 15:30:35 -080065 // This constructor is for offscreen surfaces, which are always fixed-size.
66 mFixedSize = EGL_TRUE;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000067}
68
69Surface::~Surface()
70{
71 unsubclassWindow();
72 release();
73}
74
75bool Surface::initialize()
76{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000077 if (!resetSwapChain())
78 return false;
79
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000080 return true;
81}
82
83void Surface::release()
84{
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +000085 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000086 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000087
88 if (mTexture)
89 {
90 mTexture->releaseTexImage();
91 mTexture = NULL;
92 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000093}
94
95bool Surface::resetSwapChain()
96{
daniel@transgaming.com3c720782012-10-31 18:42:34 +000097 ASSERT(!mSwapChain);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000098
daniel@transgaming.com3c720782012-10-31 18:42:34 +000099 int width;
100 int height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000101
John Bauman3dc300a2014-01-28 15:30:35 -0800102 if (!mFixedSize)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000103 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000104 RECT windowRect;
105 if (!GetClientRect(getWindowHandle(), &windowRect))
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000106 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000107 ASSERT(false);
108
109 ERR("Could not retrieve the window dimensions");
110 return error(EGL_BAD_SURFACE, false);
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000111 }
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000112
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000113 width = windowRect.right - windowRect.left;
114 height = windowRect.bottom - windowRect.top;
115 }
116 else
117 {
118 // non-window surface - size is determined at creation
119 width = mWidth;
120 height = mHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000121 }
122
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000123 mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle,
124 mConfig->mRenderTargetFormat,
125 mConfig->mDepthStencilFormat);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000126 if (!mSwapChain)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000127 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000128 return error(EGL_BAD_ALLOC, false);
129 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000130
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000131 if (!resetSwapChain(width, height))
132 {
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000133 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000134 mSwapChain = NULL;
daniel@transgaming.com2a99bfa2012-10-31 19:55:50 +0000135 return false;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000136 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000137
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000138 return true;
139}
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000140
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000141bool Surface::resizeSwapChain(int backbufferWidth, int backbufferHeight)
142{
143 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
144 ASSERT(mSwapChain);
145
John Bauman3dc300a2014-01-28 15:30:35 -0800146 EGLint status = mSwapChain->resize(std::max(1, backbufferWidth), std::max(1, backbufferHeight));
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000147
148 if (status == EGL_CONTEXT_LOST)
149 {
150 mDisplay->notifyDeviceLost();
151 return false;
152 }
153 else if (status != EGL_SUCCESS)
154 {
155 return error(status, false);
156 }
157
158 mWidth = backbufferWidth;
159 mHeight = backbufferHeight;
160
161 return true;
162}
163
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000164bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
165{
166 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
167 ASSERT(mSwapChain);
168
John Bauman3dc300a2014-01-28 15:30:35 -0800169 EGLint status = mSwapChain->reset(std::max(1, backbufferWidth), std::max(1, backbufferHeight), mSwapInterval);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000170
171 if (status == EGL_CONTEXT_LOST)
172 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000173 mRenderer->notifyDeviceLost();
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000174 return false;
175 }
176 else if (status != EGL_SUCCESS)
177 {
178 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000179 }
180
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000181 mWidth = backbufferWidth;
182 mHeight = backbufferHeight;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000183 mSwapIntervalDirty = false;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000184
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000185 return true;
186}
187
188bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
189{
190 if (!mSwapChain)
191 {
192 return true;
193 }
194
195 if (x + width > mWidth)
196 {
197 width = mWidth - x;
198 }
199
200 if (y + height > mHeight)
201 {
202 height = mHeight - y;
203 }
204
205 if (width == 0 || height == 0)
206 {
207 return true;
208 }
209
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000210 EGLint status = mSwapChain->swapRect(x, y, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000211
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000212 if (status == EGL_CONTEXT_LOST)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000213 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000214 mRenderer->notifyDeviceLost();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000215 return false;
216 }
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000217 else if (status != EGL_SUCCESS)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000218 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000219 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000220 }
221
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000222 checkForOutOfDateSwapChain();
223
224 return true;
225}
226
227HWND Surface::getWindowHandle()
228{
229 return mWindow;
230}
231
232
233#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
234#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
235
236static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
237{
238 if (message == WM_SIZE)
239 {
240 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
241 if(surf)
242 {
243 surf->checkForOutOfDateSwapChain();
244 }
245 }
246 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
247 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
248}
249
250void Surface::subclassWindow()
251{
252 if (!mWindow)
253 {
254 return;
255 }
256
257 DWORD processId;
258 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
259 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
260 {
261 return;
262 }
263
264 SetLastError(0);
265 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
266 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
267 {
268 mWindowSubclassed = false;
269 return;
270 }
271
272 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
273 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
274 mWindowSubclassed = true;
275}
276
277void Surface::unsubclassWindow()
278{
279 if(!mWindowSubclassed)
280 {
281 return;
282 }
283
284 // un-subclass
285 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
286
287 // Check the windowproc is still SurfaceWindowProc.
288 // If this assert fails, then it is likely the application has subclassed the
289 // hwnd as well and did not unsubclass before destroying its EGL context. The
290 // application should be modified to either subclass before initializing the
291 // EGL context, or to unsubclass before destroying the EGL context.
292 if(parentWndFunc)
293 {
294 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
Geoff Lang9cd19152014-05-28 15:54:34 -0400295 UNUSED_ASSERTION_VARIABLE(prevWndFunc);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000296 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
297 }
298
299 RemoveProp(mWindow, kSurfaceProperty);
300 RemoveProp(mWindow, kParentWndProc);
301 mWindowSubclassed = false;
302}
303
304bool Surface::checkForOutOfDateSwapChain()
305{
306 RECT client;
John Bauman3dc300a2014-01-28 15:30:35 -0800307 int clientWidth = getWidth();
308 int clientHeight = getHeight();
309 bool sizeDirty = false;
310 if (!mFixedSize && !IsIconic(getWindowHandle()))
John Bauman827a4712013-10-29 16:03:11 -0700311 {
312 // The window is automatically resized to 150x22 when it's minimized, but the swapchain shouldn't be resized
313 // because that's not a useful size to render to.
John Bauman3dc300a2014-01-28 15:30:35 -0800314 if (!GetClientRect(getWindowHandle(), &client))
315 {
316 ASSERT(false);
317 return false;
318 }
319
320 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
321 clientWidth = client.right - client.left;
322 clientHeight = client.bottom - client.top;
323 sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
John Bauman827a4712013-10-29 16:03:11 -0700324 }
325
Jamie Madill58e60322013-12-02 11:09:36 -0500326 bool wasDirty = (mSwapIntervalDirty || sizeDirty);
327
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000328 if (mSwapIntervalDirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000329 {
330 resetSwapChain(clientWidth, clientHeight);
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000331 }
332 else if (sizeDirty)
333 {
334 resizeSwapChain(clientWidth, clientHeight);
335 }
336
Jamie Madill58e60322013-12-02 11:09:36 -0500337 if (wasDirty)
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000338 {
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000339 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
340 {
341 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
342 }
343
344 return true;
345 }
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000346
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000347 return false;
348}
349
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000350bool Surface::swap()
351{
352 return swapRect(0, 0, mWidth, mHeight);
353}
354
355bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
356{
357 if (!mPostSubBufferSupported)
358 {
359 // Spec is not clear about how this should be handled.
360 return true;
361 }
Nicolas Capens3501c162014-05-21 13:27:15 -0400362
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000363 return swapRect(x, y, width, height);
364}
365
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000366EGLint Surface::isPostSubBufferSupported() const
367{
368 return mPostSubBufferSupported;
369}
370
daniel@transgaming.com76d3e6e2012-10-31 19:55:33 +0000371rx::SwapChain *Surface::getSwapChain() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000372{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000373 return mSwapChain;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000374}
375
376void Surface::setSwapInterval(EGLint interval)
377{
378 if (mSwapInterval == interval)
379 {
380 return;
381 }
Nicolas Capens3501c162014-05-21 13:27:15 -0400382
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000383 mSwapInterval = interval;
daniel@transgaming.com114bd462012-10-31 18:42:47 +0000384 mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval());
385 mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval());
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000386
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000387 mSwapIntervalDirty = true;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000388}
389
Nicolas Capens3501c162014-05-21 13:27:15 -0400390EGLint Surface::getConfigID() const
391{
392 return mConfig->mConfigID;
393}
394
395EGLint Surface::getWidth() const
396{
397 return mWidth;
398}
399
400EGLint Surface::getHeight() const
401{
402 return mHeight;
403}
404
405EGLint Surface::getPixelAspectRatio() const
406{
407 return mPixelAspectRatio;
408}
409
410EGLenum Surface::getRenderBuffer() const
411{
412 return mRenderBuffer;
413}
414
415EGLenum Surface::getSwapBehavior() const
416{
417 return mSwapBehavior;
418}
419
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000420EGLenum Surface::getTextureFormat() const
421{
422 return mTextureFormat;
423}
424
425EGLenum Surface::getTextureTarget() const
426{
427 return mTextureTarget;
428}
429
430void Surface::setBoundTexture(gl::Texture2D *texture)
431{
432 mTexture = texture;
433}
434
435gl::Texture2D *Surface::getBoundTexture() const
436{
437 return mTexture;
438}
439
John Bauman3dc300a2014-01-28 15:30:35 -0800440EGLint Surface::isFixedSize() const
441{
442 return mFixedSize;
443}
444
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000445EGLenum Surface::getFormat() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000446{
447 return mConfig->mRenderTargetFormat;
448}
449}