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
25namespace egl
26{
27
28Surface::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
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{
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
69Surface::~Surface()
70{
71    unsubclassWindow();
72    release();
73}
74
75bool Surface::initialize()
76{
77    if (!resetSwapChain())
78      return false;
79
80    return true;
81}
82
83void Surface::release()
84{
85    delete mSwapChain;
86    mSwapChain = NULL;
87
88    if (mTexture)
89    {
90        mTexture->releaseTexImage();
91        mTexture = NULL;
92    }
93}
94
95bool 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
141bool 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
164bool 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
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
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
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);
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
304bool 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
350bool 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    }
362
363    return swapRect(x, y, width, height);
364}
365
366EGLint Surface::isPostSubBufferSupported() const
367{
368    return mPostSubBufferSupported;
369}
370
371rx::SwapChain *Surface::getSwapChain() const
372{
373    return mSwapChain;
374}
375
376void 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
390EGLint 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
420EGLenum 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
440EGLint Surface::isFixedSize() const
441{
442    return mFixedSize;
443}
444
445EGLenum Surface::getFormat() const
446{
447    return mConfig->mRenderTargetFormat;
448}
449}
450