blob: 61b6c55c2427fb7a377593c3057e3ad89f5ba7bf [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
Cooper Partineeb1f532014-09-23 10:25:02 -070025#include "common/NativeWindow.h"
26
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000027namespace egl
28{
29
Cooper Partineeb1f532014-09-23 10:25:02 -070030Surface::Surface(Display *display, const Config *config, EGLNativeWindowType window, EGLint fixedSize, EGLint width, EGLint height, EGLint postSubBufferSupported)
31 : mDisplay(display), mConfig(config), mNativeWindow(window), mPostSubBufferSupported(postSubBufferSupported)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000032{
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +000033 mRenderer = mDisplay->getRenderer();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000034 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000035 mShareHandle = NULL;
36 mTexture = NULL;
37 mTextureFormat = EGL_NO_TEXTURE;
38 mTextureTarget = EGL_NO_TEXTURE;
39
40 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
41 mRenderBuffer = EGL_BACK_BUFFER;
42 mSwapBehavior = EGL_BUFFER_PRESERVED;
43 mSwapInterval = -1;
John Bauman3dc300a2014-01-28 15:30:35 -080044 mWidth = width;
45 mHeight = height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000046 setSwapInterval(1);
John Bauman3dc300a2014-01-28 15:30:35 -080047 mFixedSize = fixedSize;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000048
49 subclassWindow();
50}
51
52Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
Cooper Partineeb1f532014-09-23 10:25:02 -070053 : mDisplay(display), mNativeWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000054{
daniel@transgaming.com28e36922012-11-28 21:02:47 +000055 mRenderer = mDisplay->getRenderer();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000056 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000057 mWindowSubclassed = false;
58 mTexture = NULL;
59 mTextureFormat = textureFormat;
60 mTextureTarget = textureType;
61
62 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
63 mRenderBuffer = EGL_BACK_BUFFER;
64 mSwapBehavior = EGL_BUFFER_PRESERVED;
65 mSwapInterval = -1;
66 setSwapInterval(1);
John Bauman3dc300a2014-01-28 15:30:35 -080067 // This constructor is for offscreen surfaces, which are always fixed-size.
68 mFixedSize = EGL_TRUE;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000069}
70
71Surface::~Surface()
72{
73 unsubclassWindow();
74 release();
75}
76
77bool Surface::initialize()
78{
Cooper Partineeb1f532014-09-23 10:25:02 -070079 if (mNativeWindow.getNativeWindow())
80 {
81 if (!mNativeWindow.initialize())
82 {
83 return false;
84 }
85 }
86
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000087 if (!resetSwapChain())
88 return false;
89
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000090 return true;
91}
92
93void Surface::release()
94{
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +000095 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000096 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000097
98 if (mTexture)
99 {
100 mTexture->releaseTexImage();
101 mTexture = NULL;
102 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000103}
104
105bool Surface::resetSwapChain()
106{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000107 ASSERT(!mSwapChain);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000108
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000109 int width;
110 int height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000111
John Bauman3dc300a2014-01-28 15:30:35 -0800112 if (!mFixedSize)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000113 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000114 RECT windowRect;
Cooper Partineeb1f532014-09-23 10:25:02 -0700115 if (!mNativeWindow.getClientRect(&windowRect))
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000116 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000117 ASSERT(false);
118
119 ERR("Could not retrieve the window dimensions");
120 return error(EGL_BAD_SURFACE, false);
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000121 }
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000122
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000123 width = windowRect.right - windowRect.left;
124 height = windowRect.bottom - windowRect.top;
125 }
126 else
127 {
128 // non-window surface - size is determined at creation
129 width = mWidth;
130 height = mHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000131 }
132
Cooper Partineeb1f532014-09-23 10:25:02 -0700133 mSwapChain = mRenderer->createSwapChain(mNativeWindow, mShareHandle,
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000134 mConfig->mRenderTargetFormat,
135 mConfig->mDepthStencilFormat);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000136 if (!mSwapChain)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000137 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000138 return error(EGL_BAD_ALLOC, false);
139 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000140
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000141 if (!resetSwapChain(width, height))
142 {
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000143 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000144 mSwapChain = NULL;
daniel@transgaming.com2a99bfa2012-10-31 19:55:50 +0000145 return false;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000146 }
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
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000151bool Surface::resizeSwapChain(int backbufferWidth, int backbufferHeight)
152{
153 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
154 ASSERT(mSwapChain);
155
John Bauman3dc300a2014-01-28 15:30:35 -0800156 EGLint status = mSwapChain->resize(std::max(1, backbufferWidth), std::max(1, backbufferHeight));
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000157
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);
166 }
167
168 mWidth = backbufferWidth;
169 mHeight = backbufferHeight;
170
171 return true;
172}
173
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000174bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
175{
176 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
177 ASSERT(mSwapChain);
178
John Bauman3dc300a2014-01-28 15:30:35 -0800179 EGLint status = mSwapChain->reset(std::max(1, backbufferWidth), std::max(1, backbufferHeight), mSwapInterval);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000180
181 if (status == EGL_CONTEXT_LOST)
182 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000183 mRenderer->notifyDeviceLost();
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000184 return false;
185 }
186 else if (status != EGL_SUCCESS)
187 {
188 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000189 }
190
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000191 mWidth = backbufferWidth;
192 mHeight = backbufferHeight;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000193 mSwapIntervalDirty = false;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000194
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000195 return true;
196}
197
198bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
199{
200 if (!mSwapChain)
201 {
202 return true;
203 }
204
205 if (x + width > mWidth)
206 {
207 width = mWidth - x;
208 }
209
210 if (y + height > mHeight)
211 {
212 height = mHeight - y;
213 }
214
215 if (width == 0 || height == 0)
216 {
217 return true;
218 }
219
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000220 EGLint status = mSwapChain->swapRect(x, y, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000221
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000222 if (status == EGL_CONTEXT_LOST)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000223 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000224 mRenderer->notifyDeviceLost();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000225 return false;
226 }
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000227 else if (status != EGL_SUCCESS)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000228 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000229 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000230 }
231
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000232 checkForOutOfDateSwapChain();
233
234 return true;
235}
236
Cooper Partineeb1f532014-09-23 10:25:02 -0700237EGLNativeWindowType Surface::getWindowHandle()
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000238{
Cooper Partineeb1f532014-09-23 10:25:02 -0700239 return mNativeWindow.getNativeWindow();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000240}
241
242
243#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
244#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
245
246static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
247{
248 if (message == WM_SIZE)
249 {
250 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
251 if(surf)
252 {
253 surf->checkForOutOfDateSwapChain();
254 }
255 }
256 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
257 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
258}
259
260void Surface::subclassWindow()
261{
Cooper Partineeb1f532014-09-23 10:25:02 -0700262 HWND window = mNativeWindow.getNativeWindow();
263 if (!window)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000264 {
265 return;
266 }
267
268 DWORD processId;
Cooper Partineeb1f532014-09-23 10:25:02 -0700269 DWORD threadId = GetWindowThreadProcessId(window, &processId);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000270 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
271 {
272 return;
273 }
274
275 SetLastError(0);
Cooper Partineeb1f532014-09-23 10:25:02 -0700276 LONG_PTR oldWndProc = SetWindowLongPtr(window, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000277 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
278 {
279 mWindowSubclassed = false;
280 return;
281 }
282
Cooper Partineeb1f532014-09-23 10:25:02 -0700283 SetProp(window, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
284 SetProp(window, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000285 mWindowSubclassed = true;
286}
287
288void Surface::unsubclassWindow()
289{
290 if(!mWindowSubclassed)
291 {
292 return;
293 }
294
Cooper Partineeb1f532014-09-23 10:25:02 -0700295 HWND window = mNativeWindow.getNativeWindow();
296 if (!window)
297 {
298 return;
299 }
300
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000301 // un-subclass
Cooper Partineeb1f532014-09-23 10:25:02 -0700302 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(window, kParentWndProc));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000303
304 // Check the windowproc is still SurfaceWindowProc.
305 // If this assert fails, then it is likely the application has subclassed the
306 // hwnd as well and did not unsubclass before destroying its EGL context. The
307 // application should be modified to either subclass before initializing the
308 // EGL context, or to unsubclass before destroying the EGL context.
309 if(parentWndFunc)
310 {
Cooper Partineeb1f532014-09-23 10:25:02 -0700311 LONG_PTR prevWndFunc = SetWindowLongPtr(window, GWLP_WNDPROC, parentWndFunc);
Geoff Lang9cd19152014-05-28 15:54:34 -0400312 UNUSED_ASSERTION_VARIABLE(prevWndFunc);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000313 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
314 }
315
Cooper Partineeb1f532014-09-23 10:25:02 -0700316 RemoveProp(window, kSurfaceProperty);
317 RemoveProp(window, kParentWndProc);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000318 mWindowSubclassed = false;
319}
320
321bool Surface::checkForOutOfDateSwapChain()
322{
323 RECT client;
John Bauman3dc300a2014-01-28 15:30:35 -0800324 int clientWidth = getWidth();
325 int clientHeight = getHeight();
326 bool sizeDirty = false;
Cooper Partineeb1f532014-09-23 10:25:02 -0700327 if (!mFixedSize && !mNativeWindow.isIconic())
John Bauman827a4712013-10-29 16:03:11 -0700328 {
329 // The window is automatically resized to 150x22 when it's minimized, but the swapchain shouldn't be resized
330 // because that's not a useful size to render to.
Cooper Partineeb1f532014-09-23 10:25:02 -0700331 if (!mNativeWindow.getClientRect(&client))
John Bauman3dc300a2014-01-28 15:30:35 -0800332 {
333 ASSERT(false);
334 return false;
335 }
336
337 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
338 clientWidth = client.right - client.left;
339 clientHeight = client.bottom - client.top;
340 sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
John Bauman827a4712013-10-29 16:03:11 -0700341 }
342
Jamie Madill58e60322013-12-02 11:09:36 -0500343 bool wasDirty = (mSwapIntervalDirty || sizeDirty);
344
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000345 if (mSwapIntervalDirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000346 {
347 resetSwapChain(clientWidth, clientHeight);
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000348 }
349 else if (sizeDirty)
350 {
351 resizeSwapChain(clientWidth, clientHeight);
352 }
353
Jamie Madill58e60322013-12-02 11:09:36 -0500354 if (wasDirty)
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000355 {
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000356 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
357 {
358 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
359 }
360
361 return true;
362 }
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000363
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000364 return false;
365}
366
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000367bool Surface::swap()
368{
369 return swapRect(0, 0, mWidth, mHeight);
370}
371
372bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
373{
374 if (!mPostSubBufferSupported)
375 {
376 // Spec is not clear about how this should be handled.
377 return true;
378 }
Nicolas Capens3501c162014-05-21 13:27:15 -0400379
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000380 return swapRect(x, y, width, height);
381}
382
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000383EGLint Surface::isPostSubBufferSupported() const
384{
385 return mPostSubBufferSupported;
386}
387
daniel@transgaming.com76d3e6e2012-10-31 19:55:33 +0000388rx::SwapChain *Surface::getSwapChain() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000389{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000390 return mSwapChain;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000391}
392
393void Surface::setSwapInterval(EGLint interval)
394{
395 if (mSwapInterval == interval)
396 {
397 return;
398 }
Nicolas Capens3501c162014-05-21 13:27:15 -0400399
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000400 mSwapInterval = interval;
daniel@transgaming.com114bd462012-10-31 18:42:47 +0000401 mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval());
402 mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval());
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000403
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000404 mSwapIntervalDirty = true;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000405}
406
Nicolas Capens3501c162014-05-21 13:27:15 -0400407EGLint Surface::getConfigID() const
408{
409 return mConfig->mConfigID;
410}
411
412EGLint Surface::getWidth() const
413{
414 return mWidth;
415}
416
417EGLint Surface::getHeight() const
418{
419 return mHeight;
420}
421
422EGLint Surface::getPixelAspectRatio() const
423{
424 return mPixelAspectRatio;
425}
426
427EGLenum Surface::getRenderBuffer() const
428{
429 return mRenderBuffer;
430}
431
432EGLenum Surface::getSwapBehavior() const
433{
434 return mSwapBehavior;
435}
436
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000437EGLenum Surface::getTextureFormat() const
438{
439 return mTextureFormat;
440}
441
442EGLenum Surface::getTextureTarget() const
443{
444 return mTextureTarget;
445}
446
447void Surface::setBoundTexture(gl::Texture2D *texture)
448{
449 mTexture = texture;
450}
451
452gl::Texture2D *Surface::getBoundTexture() const
453{
454 return mTexture;
455}
456
John Bauman3dc300a2014-01-28 15:30:35 -0800457EGLint Surface::isFixedSize() const
458{
459 return mFixedSize;
460}
461
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000462EGLenum Surface::getFormat() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000463{
464 return mConfig->mRenderTargetFormat;
465}
466}