• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2002-2014 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 <algorithm>
14 
15 #include "libEGL/Surface.h"
16 
17 #include "common/debug.h"
18 #include "libGLESv2/Texture.h"
19 #include "libGLESv2/renderer/SwapChain.h"
20 #include "libGLESv2/main.h"
21 
22 #include "libEGL/main.h"
23 #include "libEGL/Display.h"
24 
25 namespace egl
26 {
27 
Surface(Display * display,const Config * config,HWND window,EGLint fixedSize,EGLint width,EGLint height,EGLint postSubBufferSupported)28 Surface::Surface(Display *display, const Config *config, HWND window, EGLint fixedSize, EGLint width, EGLint height, 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 = width;
43     mHeight = height;
44     setSwapInterval(1);
45     mFixedSize = fixedSize;
46 
47     subclassWindow();
48 }
49 
Surface(Display * display,const Config * config,HANDLE shareHandle,EGLint width,EGLint height,EGLenum textureFormat,EGLenum textureType)50 Surface::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 {
53     mRenderer = mDisplay->getRenderer();
54     mSwapChain = NULL;
55     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);
65     // This constructor is for offscreen surfaces, which are always fixed-size.
66     mFixedSize = EGL_TRUE;
67 }
68 
~Surface()69 Surface::~Surface()
70 {
71     unsubclassWindow();
72     release();
73 }
74 
initialize()75 bool Surface::initialize()
76 {
77     if (!resetSwapChain())
78       return false;
79 
80     return true;
81 }
82 
release()83 void Surface::release()
84 {
85     delete mSwapChain;
86     mSwapChain = NULL;
87 
88     if (mTexture)
89     {
90         mTexture->releaseTexImage();
91         mTexture = NULL;
92     }
93 }
94 
resetSwapChain()95 bool Surface::resetSwapChain()
96 {
97     ASSERT(!mSwapChain);
98 
99     int width;
100     int height;
101 
102     if (!mFixedSize)
103     {
104         RECT windowRect;
105         if (!GetClientRect(getWindowHandle(), &windowRect))
106         {
107             ASSERT(false);
108 
109             ERR("Could not retrieve the window dimensions");
110             return error(EGL_BAD_SURFACE, false);
111         }
112 
113         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;
121     }
122 
123     mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle,
124                                             mConfig->mRenderTargetFormat,
125                                             mConfig->mDepthStencilFormat);
126     if (!mSwapChain)
127     {
128         return error(EGL_BAD_ALLOC, false);
129     }
130 
131     if (!resetSwapChain(width, height))
132     {
133         delete mSwapChain;
134         mSwapChain = NULL;
135         return false;
136     }
137 
138     return true;
139 }
140 
resizeSwapChain(int backbufferWidth,int backbufferHeight)141 bool Surface::resizeSwapChain(int backbufferWidth, int backbufferHeight)
142 {
143     ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
144     ASSERT(mSwapChain);
145 
146     EGLint status = mSwapChain->resize(std::max(1, backbufferWidth), std::max(1, backbufferHeight));
147 
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 
resetSwapChain(int backbufferWidth,int backbufferHeight)164 bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
165 {
166     ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
167     ASSERT(mSwapChain);
168 
169     EGLint status = mSwapChain->reset(std::max(1, backbufferWidth), std::max(1, backbufferHeight), mSwapInterval);
170 
171     if (status == EGL_CONTEXT_LOST)
172     {
173         mRenderer->notifyDeviceLost();
174         return false;
175     }
176     else if (status != EGL_SUCCESS)
177     {
178         return error(status, false);
179     }
180 
181     mWidth = backbufferWidth;
182     mHeight = backbufferHeight;
183     mSwapIntervalDirty = false;
184 
185     return true;
186 }
187 
swapRect(EGLint x,EGLint y,EGLint width,EGLint height)188 bool 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 
210     EGLint status = mSwapChain->swapRect(x, y, width, height);
211 
212     if (status == EGL_CONTEXT_LOST)
213     {
214         mRenderer->notifyDeviceLost();
215         return false;
216     }
217     else if (status != EGL_SUCCESS)
218     {
219         return error(status, false);
220     }
221 
222     checkForOutOfDateSwapChain();
223 
224     return true;
225 }
226 
getWindowHandle()227 HWND Surface::getWindowHandle()
228 {
229     return mWindow;
230 }
231 
232 
233 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
234 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
235 
SurfaceWindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)236 static 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 
subclassWindow()250 void 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 
unsubclassWindow()277 void 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);
295         UNUSED_ASSERTION_VARIABLE(prevWndFunc);
296         ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
297     }
298 
299     RemoveProp(mWindow, kSurfaceProperty);
300     RemoveProp(mWindow, kParentWndProc);
301     mWindowSubclassed = false;
302 }
303 
checkForOutOfDateSwapChain()304 bool Surface::checkForOutOfDateSwapChain()
305 {
306     RECT client;
307     int clientWidth = getWidth();
308     int clientHeight = getHeight();
309     bool sizeDirty = false;
310     if (!mFixedSize && !IsIconic(getWindowHandle()))
311     {
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.
314         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();
324     }
325 
326     bool wasDirty = (mSwapIntervalDirty || sizeDirty);
327 
328     if (mSwapIntervalDirty)
329     {
330         resetSwapChain(clientWidth, clientHeight);
331     }
332     else if (sizeDirty)
333     {
334         resizeSwapChain(clientWidth, clientHeight);
335     }
336 
337     if (wasDirty)
338     {
339         if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
340         {
341             glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
342         }
343 
344         return true;
345     }
346 
347     return false;
348 }
349 
swap()350 bool Surface::swap()
351 {
352     return swapRect(0, 0, mWidth, mHeight);
353 }
354 
postSubBuffer(EGLint x,EGLint y,EGLint width,EGLint height)355 bool 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     }
362 
363     return swapRect(x, y, width, height);
364 }
365 
isPostSubBufferSupported() const366 EGLint Surface::isPostSubBufferSupported() const
367 {
368     return mPostSubBufferSupported;
369 }
370 
getSwapChain() const371 rx::SwapChain *Surface::getSwapChain() const
372 {
373     return mSwapChain;
374 }
375 
setSwapInterval(EGLint interval)376 void Surface::setSwapInterval(EGLint interval)
377 {
378     if (mSwapInterval == interval)
379     {
380         return;
381     }
382 
383     mSwapInterval = interval;
384     mSwapInterval = std::max(mSwapInterval, mRenderer->getMinSwapInterval());
385     mSwapInterval = std::min(mSwapInterval, mRenderer->getMaxSwapInterval());
386 
387     mSwapIntervalDirty = true;
388 }
389 
getConfigID() const390 EGLint Surface::getConfigID() const
391 {
392     return mConfig->mConfigID;
393 }
394 
getWidth() const395 EGLint Surface::getWidth() const
396 {
397     return mWidth;
398 }
399 
getHeight() const400 EGLint Surface::getHeight() const
401 {
402     return mHeight;
403 }
404 
getPixelAspectRatio() const405 EGLint Surface::getPixelAspectRatio() const
406 {
407     return mPixelAspectRatio;
408 }
409 
getRenderBuffer() const410 EGLenum Surface::getRenderBuffer() const
411 {
412     return mRenderBuffer;
413 }
414 
getSwapBehavior() const415 EGLenum Surface::getSwapBehavior() const
416 {
417     return mSwapBehavior;
418 }
419 
getTextureFormat() const420 EGLenum Surface::getTextureFormat() const
421 {
422     return mTextureFormat;
423 }
424 
getTextureTarget() const425 EGLenum Surface::getTextureTarget() const
426 {
427     return mTextureTarget;
428 }
429 
setBoundTexture(gl::Texture2D * texture)430 void Surface::setBoundTexture(gl::Texture2D *texture)
431 {
432     mTexture = texture;
433 }
434 
getBoundTexture() const435 gl::Texture2D *Surface::getBoundTexture() const
436 {
437     return mTexture;
438 }
439 
isFixedSize() const440 EGLint Surface::isFixedSize() const
441 {
442     return mFixedSize;
443 }
444 
getFormat() const445 EGLenum Surface::getFormat() const
446 {
447     return mConfig->mRenderTargetFormat;
448 }
449 }
450