blob: c423be19bb1559f710617e0a01fa3b9de5771a48 [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
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000023namespace egl
24{
25
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000026Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported)
27 : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported)
28{
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +000029 mRenderer = mDisplay->getRenderer();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000030 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{
daniel@transgaming.com28e36922012-11-28 21:02:47 +000050 mRenderer = mDisplay->getRenderer();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000051 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000052 mWindowSubclassed = false;
53 mTexture = NULL;
54 mTextureFormat = textureFormat;
55 mTextureTarget = textureType;
56
57 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
58 mRenderBuffer = EGL_BACK_BUFFER;
59 mSwapBehavior = EGL_BUFFER_PRESERVED;
60 mSwapInterval = -1;
61 setSwapInterval(1);
62}
63
64Surface::~Surface()
65{
66 unsubclassWindow();
67 release();
68}
69
70bool Surface::initialize()
71{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000072 if (!resetSwapChain())
73 return false;
74
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000075 return true;
76}
77
78void Surface::release()
79{
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +000080 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +000081 mSwapChain = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000082
83 if (mTexture)
84 {
85 mTexture->releaseTexImage();
86 mTexture = NULL;
87 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000088}
89
90bool Surface::resetSwapChain()
91{
daniel@transgaming.com3c720782012-10-31 18:42:34 +000092 ASSERT(!mSwapChain);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000093
daniel@transgaming.com3c720782012-10-31 18:42:34 +000094 int width;
95 int height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000096
97 if (mWindow)
98 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +000099 RECT windowRect;
100 if (!GetClientRect(getWindowHandle(), &windowRect))
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000101 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000102 ASSERT(false);
103
104 ERR("Could not retrieve the window dimensions");
105 return error(EGL_BAD_SURFACE, false);
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000106 }
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000107
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000108 width = windowRect.right - windowRect.left;
109 height = windowRect.bottom - windowRect.top;
110 }
111 else
112 {
113 // non-window surface - size is determined at creation
114 width = mWidth;
115 height = mHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000116 }
117
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000118 mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle,
119 mConfig->mRenderTargetFormat,
120 mConfig->mDepthStencilFormat);
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000121 if (!mSwapChain)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000122 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000123 return error(EGL_BAD_ALLOC, false);
124 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000125
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000126 if (!resetSwapChain(width, height))
127 {
daniel@transgaming.comb9bb2792012-11-28 19:36:49 +0000128 delete mSwapChain;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000129 mSwapChain = NULL;
daniel@transgaming.com2a99bfa2012-10-31 19:55:50 +0000130 return false;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000131 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000132
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000133 return true;
134}
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000135
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000136bool Surface::resizeSwapChain(int backbufferWidth, int backbufferHeight)
137{
138 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
139 ASSERT(mSwapChain);
140
141 EGLint status = mSwapChain->resize(backbufferWidth, backbufferHeight);
142
143 if (status == EGL_CONTEXT_LOST)
144 {
145 mDisplay->notifyDeviceLost();
146 return false;
147 }
148 else if (status != EGL_SUCCESS)
149 {
150 return error(status, false);
151 }
152
153 mWidth = backbufferWidth;
154 mHeight = backbufferHeight;
155
156 return true;
157}
158
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000159bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
160{
161 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
162 ASSERT(mSwapChain);
163
164 EGLint status = mSwapChain->reset(backbufferWidth, backbufferHeight, mSwapInterval);
165
166 if (status == EGL_CONTEXT_LOST)
167 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000168 mRenderer->notifyDeviceLost();
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000169 return false;
170 }
171 else if (status != EGL_SUCCESS)
172 {
173 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000174 }
175
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000176 mWidth = backbufferWidth;
177 mHeight = backbufferHeight;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000178 mSwapIntervalDirty = false;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000179
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000180 return true;
181}
182
183bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
184{
185 if (!mSwapChain)
186 {
187 return true;
188 }
189
190 if (x + width > mWidth)
191 {
192 width = mWidth - x;
193 }
194
195 if (y + height > mHeight)
196 {
197 height = mHeight - y;
198 }
199
200 if (width == 0 || height == 0)
201 {
202 return true;
203 }
204
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000205 EGLint status = mSwapChain->swapRect(x, y, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000206
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000207 if (status == EGL_CONTEXT_LOST)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000208 {
shannon.woods@transgaming.comeb049e22013-02-28 23:04:49 +0000209 mRenderer->notifyDeviceLost();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000210 return false;
211 }
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000212 else if (status != EGL_SUCCESS)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000213 {
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000214 return error(status, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000215 }
216
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000217 checkForOutOfDateSwapChain();
218
219 return true;
220}
221
222HWND Surface::getWindowHandle()
223{
224 return mWindow;
225}
226
227
228#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
229#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
230
231static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
232{
233 if (message == WM_SIZE)
234 {
235 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
236 if(surf)
237 {
238 surf->checkForOutOfDateSwapChain();
239 }
240 }
241 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
242 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
243}
244
245void Surface::subclassWindow()
246{
247 if (!mWindow)
248 {
249 return;
250 }
251
252 DWORD processId;
253 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
254 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
255 {
256 return;
257 }
258
259 SetLastError(0);
260 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
261 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
262 {
263 mWindowSubclassed = false;
264 return;
265 }
266
267 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
268 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
269 mWindowSubclassed = true;
270}
271
272void Surface::unsubclassWindow()
273{
274 if(!mWindowSubclassed)
275 {
276 return;
277 }
278
279 // un-subclass
280 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
281
282 // Check the windowproc is still SurfaceWindowProc.
283 // If this assert fails, then it is likely the application has subclassed the
284 // hwnd as well and did not unsubclass before destroying its EGL context. The
285 // application should be modified to either subclass before initializing the
286 // EGL context, or to unsubclass before destroying the EGL context.
287 if(parentWndFunc)
288 {
289 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
290 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
291 }
292
293 RemoveProp(mWindow, kSurfaceProperty);
294 RemoveProp(mWindow, kParentWndProc);
295 mWindowSubclassed = false;
296}
297
298bool Surface::checkForOutOfDateSwapChain()
299{
300 RECT client;
301 if (!GetClientRect(getWindowHandle(), &client))
302 {
303 ASSERT(false);
304 return false;
305 }
306
307 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
308 int clientWidth = client.right - client.left;
309 int clientHeight = client.bottom - client.top;
310 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
311
Jamie Madill58e60322013-12-02 11:09:36 -0500312 bool wasDirty = (mSwapIntervalDirty || sizeDirty);
313
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000314 if (mSwapIntervalDirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000315 {
316 resetSwapChain(clientWidth, clientHeight);
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000317 }
318 else if (sizeDirty)
319 {
320 resizeSwapChain(clientWidth, clientHeight);
321 }
322
Jamie Madill58e60322013-12-02 11:09:36 -0500323 if (wasDirty)
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000324 {
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000325 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
326 {
327 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
328 }
329
330 return true;
331 }
shannon.woods@transgaming.comc71ca752013-02-28 23:06:50 +0000332
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000333 return false;
334}
335
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000336bool Surface::swap()
337{
338 return swapRect(0, 0, mWidth, mHeight);
339}
340
341bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
342{
343 if (!mPostSubBufferSupported)
344 {
345 // Spec is not clear about how this should be handled.
346 return true;
347 }
348
349 return swapRect(x, y, width, height);
350}
351
352EGLint Surface::getWidth() const
353{
354 return mWidth;
355}
356
357EGLint Surface::getHeight() const
358{
359 return mHeight;
360}
361
362EGLint Surface::isPostSubBufferSupported() const
363{
364 return mPostSubBufferSupported;
365}
366
daniel@transgaming.com76d3e6e2012-10-31 19:55:33 +0000367rx::SwapChain *Surface::getSwapChain() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000368{
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000369 return mSwapChain;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000370}
371
372void Surface::setSwapInterval(EGLint interval)
373{
374 if (mSwapInterval == interval)
375 {
376 return;
377 }
378
379 mSwapInterval = interval;
daniel@transgaming.com114bd462012-10-31 18:42:47 +0000380 mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval());
381 mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval());
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000382
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000383 mSwapIntervalDirty = true;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000384}
385
386EGLenum Surface::getTextureFormat() const
387{
388 return mTextureFormat;
389}
390
391EGLenum Surface::getTextureTarget() const
392{
393 return mTextureTarget;
394}
395
396void Surface::setBoundTexture(gl::Texture2D *texture)
397{
398 mTexture = texture;
399}
400
401gl::Texture2D *Surface::getBoundTexture() const
402{
403 return mTexture;
404}
405
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000406EGLenum Surface::getFormat() const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000407{
408 return mConfig->mRenderTargetFormat;
409}
410}