|
1 // |
|
2 // Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved. |
|
3 // 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" |
|
17 #include "libGLESv2/renderer/SwapChain.h" |
|
18 #include "libGLESv2/main.h" |
|
19 |
|
20 #include "libEGL/main.h" |
|
21 #include "libEGL/Display.h" |
|
22 |
|
23 #include <algorithm> |
|
24 |
|
25 namespace egl |
|
26 { |
|
27 |
|
28 Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported) |
|
29 : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported) |
|
30 { |
|
31 mRenderer = mDisplay->getRenderer(); |
|
32 mSwapChain = NULL; |
|
33 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; |
|
42 mWidth = -1; |
|
43 mHeight = -1; |
|
44 setSwapInterval(1); |
|
45 |
|
46 subclassWindow(); |
|
47 } |
|
48 |
|
49 Surface::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 { |
|
52 mRenderer = mDisplay->getRenderer(); |
|
53 mSwapChain = NULL; |
|
54 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 |
|
66 Surface::~Surface() |
|
67 { |
|
68 unsubclassWindow(); |
|
69 release(); |
|
70 } |
|
71 |
|
72 bool Surface::initialize() |
|
73 { |
|
74 if (!resetSwapChain()) |
|
75 return false; |
|
76 |
|
77 return true; |
|
78 } |
|
79 |
|
80 void Surface::release() |
|
81 { |
|
82 delete mSwapChain; |
|
83 mSwapChain = NULL; |
|
84 |
|
85 if (mTexture) |
|
86 { |
|
87 mTexture->releaseTexImage(); |
|
88 mTexture = NULL; |
|
89 } |
|
90 } |
|
91 |
|
92 bool Surface::resetSwapChain() |
|
93 { |
|
94 ASSERT(!mSwapChain); |
|
95 |
|
96 int width; |
|
97 int height; |
|
98 |
|
99 if (mWindow) |
|
100 { |
|
101 RECT windowRect; |
|
102 if (!GetClientRect(getWindowHandle(), &windowRect)) |
|
103 { |
|
104 ASSERT(false); |
|
105 |
|
106 ERR("Could not retrieve the window dimensions"); |
|
107 return error(EGL_BAD_SURFACE, false); |
|
108 } |
|
109 |
|
110 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; |
|
118 } |
|
119 |
|
120 mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle, |
|
121 mConfig->mRenderTargetFormat, |
|
122 mConfig->mDepthStencilFormat); |
|
123 if (!mSwapChain) |
|
124 { |
|
125 return error(EGL_BAD_ALLOC, false); |
|
126 } |
|
127 |
|
128 if (!resetSwapChain(width, height)) |
|
129 { |
|
130 delete mSwapChain; |
|
131 mSwapChain = NULL; |
|
132 return false; |
|
133 } |
|
134 |
|
135 return true; |
|
136 } |
|
137 |
|
138 bool 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 |
|
161 bool 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 { |
|
170 mRenderer->notifyDeviceLost(); |
|
171 return false; |
|
172 } |
|
173 else if (status != EGL_SUCCESS) |
|
174 { |
|
175 return error(status, false); |
|
176 } |
|
177 |
|
178 mWidth = backbufferWidth; |
|
179 mHeight = backbufferHeight; |
|
180 mSwapIntervalDirty = false; |
|
181 |
|
182 return true; |
|
183 } |
|
184 |
|
185 bool 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 |
|
207 EGLint status = mSwapChain->swapRect(x, y, width, height); |
|
208 |
|
209 if (status == EGL_CONTEXT_LOST) |
|
210 { |
|
211 mRenderer->notifyDeviceLost(); |
|
212 return false; |
|
213 } |
|
214 else if (status != EGL_SUCCESS) |
|
215 { |
|
216 return error(status, false); |
|
217 } |
|
218 |
|
219 checkForOutOfDateSwapChain(); |
|
220 |
|
221 return true; |
|
222 } |
|
223 |
|
224 HWND Surface::getWindowHandle() |
|
225 { |
|
226 return mWindow; |
|
227 } |
|
228 |
|
229 |
|
230 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner") |
|
231 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc") |
|
232 |
|
233 static 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 |
|
247 void 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 |
|
274 void 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 |
|
300 bool 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 |
|
314 if (mSwapIntervalDirty) |
|
315 { |
|
316 resetSwapChain(clientWidth, clientHeight); |
|
317 } |
|
318 else if (sizeDirty) |
|
319 { |
|
320 resizeSwapChain(clientWidth, clientHeight); |
|
321 } |
|
322 |
|
323 if (mSwapIntervalDirty || sizeDirty) |
|
324 { |
|
325 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this) |
|
326 { |
|
327 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this); |
|
328 } |
|
329 |
|
330 return true; |
|
331 } |
|
332 |
|
333 return false; |
|
334 } |
|
335 |
|
336 bool Surface::swap() |
|
337 { |
|
338 return swapRect(0, 0, mWidth, mHeight); |
|
339 } |
|
340 |
|
341 bool 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 |
|
352 EGLint Surface::getWidth() const |
|
353 { |
|
354 return mWidth; |
|
355 } |
|
356 |
|
357 EGLint Surface::getHeight() const |
|
358 { |
|
359 return mHeight; |
|
360 } |
|
361 |
|
362 EGLint Surface::isPostSubBufferSupported() const |
|
363 { |
|
364 return mPostSubBufferSupported; |
|
365 } |
|
366 |
|
367 rx::SwapChain *Surface::getSwapChain() const |
|
368 { |
|
369 return mSwapChain; |
|
370 } |
|
371 |
|
372 void Surface::setSwapInterval(EGLint interval) |
|
373 { |
|
374 if (mSwapInterval == interval) |
|
375 { |
|
376 return; |
|
377 } |
|
378 |
|
379 mSwapInterval = interval; |
|
380 mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval()); |
|
381 mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval()); |
|
382 |
|
383 mSwapIntervalDirty = true; |
|
384 } |
|
385 |
|
386 EGLenum Surface::getTextureFormat() const |
|
387 { |
|
388 return mTextureFormat; |
|
389 } |
|
390 |
|
391 EGLenum Surface::getTextureTarget() const |
|
392 { |
|
393 return mTextureTarget; |
|
394 } |
|
395 |
|
396 void Surface::setBoundTexture(gl::Texture2D *texture) |
|
397 { |
|
398 mTexture = texture; |
|
399 } |
|
400 |
|
401 gl::Texture2D *Surface::getBoundTexture() const |
|
402 { |
|
403 return mTexture; |
|
404 } |
|
405 |
|
406 EGLenum Surface::getFormat() const |
|
407 { |
|
408 return mConfig->mRenderTargetFormat; |
|
409 } |
|
410 } |