initial import
[vuplus_webkit] / Source / ThirdParty / ANGLE / src / libEGL / Surface.cpp
1 //
2 // Copyright (c) 2002-2010 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
18 #include "libEGL/main.h"
19 #include "libEGL/Display.h"
20
21 #include <dwmapi.h>
22
23 namespace egl
24 {
25
26 namespace
27 {
28 const int versionWindowsVista = MAKEWORD(0x00, 0x06);
29 const int versionWindows7 = MAKEWORD(0x01, 0x06);
30
31 // Return the version of the operating system in a format suitable for ordering
32 // comparison.
33 int getComparableOSVersion()
34 {
35     DWORD version = GetVersion();
36     int majorVersion = LOBYTE(LOWORD(version));
37     int minorVersion = HIBYTE(LOWORD(version));
38     return MAKEWORD(minorVersion, majorVersion);
39 }
40 }
41
42 Surface::Surface(Display *display, const Config *config, HWND window) 
43     : mDisplay(display), mConfig(config), mWindow(window)
44 {
45     mSwapChain = NULL;
46     mDepthStencil = NULL;
47     mRenderTarget = NULL;
48     mOffscreenTexture = NULL;
49     mShareHandle = NULL;
50     mTexture = NULL;
51     mTextureFormat = EGL_NO_TEXTURE;
52     mTextureTarget = EGL_NO_TEXTURE;
53
54     mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
55     mRenderBuffer = EGL_BACK_BUFFER;
56     mSwapBehavior = EGL_BUFFER_PRESERVED;
57     mSwapInterval = -1;
58     setSwapInterval(1);
59
60     subclassWindow();
61 }
62
63 Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
64     : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height)
65 {
66     mSwapChain = NULL;
67     mDepthStencil = NULL;
68     mRenderTarget = NULL;
69     mOffscreenTexture = NULL;
70     mWindowSubclassed = false;
71     mTexture = NULL;
72     mTextureFormat = textureFormat;
73     mTextureTarget = textureType;
74
75     mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
76     mRenderBuffer = EGL_BACK_BUFFER;
77     mSwapBehavior = EGL_BUFFER_PRESERVED;
78     mSwapInterval = -1;
79     setSwapInterval(1);
80 }
81
82 Surface::~Surface()
83 {
84     unsubclassWindow();
85     release();
86 }
87
88 bool Surface::initialize()
89 {
90     ASSERT(!mSwapChain && !mOffscreenTexture && !mDepthStencil);
91
92     if (!resetSwapChain())
93       return false;
94
95     // Modify present parameters for this window, if we are composited,
96     // to minimize the amount of queuing done by DWM between our calls to
97     // present and the actual screen.
98     if (mWindow && (getComparableOSVersion() >= versionWindowsVista)) {
99       BOOL isComposited;
100       HRESULT result = DwmIsCompositionEnabled(&isComposited);
101       if (SUCCEEDED(result) && isComposited) {
102         DWM_PRESENT_PARAMETERS presentParams;
103         memset(&presentParams, 0, sizeof(presentParams));
104         presentParams.cbSize = sizeof(DWM_PRESENT_PARAMETERS);
105         presentParams.cBuffer = 2;
106
107         result = DwmSetPresentParameters(mWindow, &presentParams);
108         if (FAILED(result))
109           ERR("Unable to set present parameters: %081X", result);
110       }
111     }
112
113     return true;
114 }
115
116 void Surface::release()
117 {
118     if (mSwapChain)
119     {
120         mSwapChain->Release();
121         mSwapChain = NULL;
122     }
123
124     if (mDepthStencil)
125     {
126         mDepthStencil->Release();
127         mDepthStencil = NULL;
128     }
129
130     if (mRenderTarget)
131     {
132         mRenderTarget->Release();
133         mRenderTarget = NULL;
134     }
135
136     if (mOffscreenTexture)
137     {
138         mOffscreenTexture->Release();
139         mOffscreenTexture = NULL;
140     }
141
142     if (mTexture)
143     {
144         mTexture->releaseTexImage();
145         mTexture = NULL;
146     }
147 }
148
149 bool Surface::resetSwapChain()
150 {
151     if (!mWindow)
152     {
153         return resetSwapChain(mWidth, mHeight);
154     }
155
156     RECT windowRect;
157     if (!GetClientRect(getWindowHandle(), &windowRect))
158     {
159         ASSERT(false);
160
161         ERR("Could not retrieve the window dimensions");
162         return false;
163     }
164
165     return resetSwapChain(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
166 }
167
168 bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
169 {
170     IDirect3DDevice9 *device = mDisplay->getDevice();
171
172     if (device == NULL)
173     {
174         return false;
175     }
176
177     // Evict all non-render target textures to system memory and release all resources
178     // before reallocating them to free up as much video memory as possible.
179     device->EvictManagedResources();
180     release();
181
182     D3DPRESENT_PARAMETERS presentParameters = {0};
183     HRESULT result;
184
185     bool useFlipEx = (getComparableOSVersion() >= versionWindows7) && mDisplay->isD3d9ExDevice();
186
187     // FlipEx causes unseemly stretching when resizing windows AND when one
188     // draws outside of the WM_PAINT callback. While this is seldom a problem in
189     // single process applications, it is particuarly noticeable in multiprocess
190     // applications. Therefore, if the creator process of our window is not in
191     // the current process, disable use of FlipEx.
192     DWORD windowPID;
193     GetWindowThreadProcessId(mWindow, &windowPID);
194     if(windowPID != GetCurrentProcessId())
195     useFlipEx = false;
196
197     presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat;
198     // We set BackBufferCount = 1 even when we use D3DSWAPEFFECT_FLIPEX.
199     // We do this because DirectX docs are a bit vague whether to set this to 1
200     // or 2. The runtime seems to accept 1, so we speculate that either it is
201     // forcing it to 2 without telling us, or better, doing something smart
202     // behind the scenes knowing that we don't need more.
203     presentParameters.BackBufferCount = 1;
204     presentParameters.BackBufferFormat = mConfig->mRenderTargetFormat;
205     presentParameters.EnableAutoDepthStencil = FALSE;
206     presentParameters.Flags = 0;
207     presentParameters.hDeviceWindow = getWindowHandle();
208     presentParameters.MultiSampleQuality = 0;                  // FIXME: Unimplemented
209     presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;   // FIXME: Unimplemented
210     presentParameters.PresentationInterval = mPresentInterval;
211     // Use flipEx on Win7 or greater.
212     if(useFlipEx)
213       presentParameters.SwapEffect = D3DSWAPEFFECT_FLIPEX;
214     else
215       presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
216     presentParameters.Windowed = TRUE;
217     presentParameters.BackBufferWidth = backbufferWidth;
218     presentParameters.BackBufferHeight = backbufferHeight;
219
220     if (mWindow)
221     {
222         result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
223     } else {
224         HANDLE *pShareHandle = NULL;
225         if (mDisplay->isD3d9ExDevice()) {
226             pShareHandle = &mShareHandle;
227         }
228
229         result = device->CreateTexture(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 1, D3DUSAGE_RENDERTARGET,
230                                        presentParameters.BackBufferFormat, D3DPOOL_DEFAULT, &mOffscreenTexture, pShareHandle);
231     }
232
233     if (FAILED(result))
234     {
235         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
236
237         ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
238         release();
239         return error(EGL_BAD_ALLOC, false);
240     }
241
242     if (mConfig->mDepthStencilFormat != D3DFMT_UNKNOWN)
243     {
244         result = device->CreateDepthStencilSurface(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight,
245                                                    presentParameters.AutoDepthStencilFormat, presentParameters.MultiSampleType,
246                                                    presentParameters.MultiSampleQuality, FALSE, &mDepthStencil, NULL);
247     }
248
249     if (FAILED(result))
250     {
251         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
252
253         ERR("Could not create depthstencil surface for new swap chain: %08lX", result);
254         release();
255         return error(EGL_BAD_ALLOC, false);
256     }
257
258     if (mWindow) {
259         mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mRenderTarget);
260         InvalidateRect(mWindow, NULL, FALSE);
261     } else {
262         mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
263     }
264
265     mWidth = presentParameters.BackBufferWidth;
266     mHeight = presentParameters.BackBufferHeight;
267
268     mPresentIntervalDirty = false;
269     return true;
270 }
271
272 HWND Surface::getWindowHandle()
273 {
274     return mWindow;
275 }
276
277
278 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
279 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
280
281 static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
282 {
283   if (message == WM_SIZE)
284   {
285       Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
286       if(surf)
287       {
288           surf->checkForOutOfDateSwapChain();
289       }
290   }
291   WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
292   return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
293 }
294
295 void Surface::subclassWindow()
296 {
297     if (!mWindow)
298     {
299         return;
300     }
301
302     DWORD processId;
303     DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
304     if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
305     {
306         return;
307     }
308
309     SetLastError(0);
310     LONG oldWndProc = SetWindowLong(mWindow, GWL_WNDPROC, reinterpret_cast<LONG>(SurfaceWindowProc));
311     if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
312     {
313         mWindowSubclassed = false;
314         return;
315     }
316
317     SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
318     SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
319     mWindowSubclassed = true;
320 }
321
322 void Surface::unsubclassWindow()
323 {
324     if(!mWindowSubclassed)
325     {
326         return;
327     }
328
329     // un-subclass
330     LONG parentWndFunc = reinterpret_cast<LONG>(GetProp(mWindow, kParentWndProc));
331
332     // Check the windowproc is still SurfaceWindowProc.
333     // If this assert fails, then it is likely the application has subclassed the
334     // hwnd as well and did not unsubclass before destroying its EGL context. The
335     // application should be modified to either subclass before initializing the
336     // EGL context, or to unsubclass before destroying the EGL context.
337     if(parentWndFunc)
338     {
339         LONG prevWndFunc = SetWindowLong(mWindow, GWL_WNDPROC, parentWndFunc);
340         ASSERT(prevWndFunc == reinterpret_cast<LONG>(SurfaceWindowProc));
341     }
342
343     RemoveProp(mWindow, kSurfaceProperty);
344     RemoveProp(mWindow, kParentWndProc);
345     mWindowSubclassed = false;
346 }
347
348 bool Surface::checkForOutOfDateSwapChain()
349 {
350     RECT client;
351     if (!GetClientRect(getWindowHandle(), &client))
352     {
353         ASSERT(false);
354         return false;
355     }
356
357     // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
358     int clientWidth = client.right - client.left;
359     int clientHeight = client.bottom - client.top;
360     bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
361
362     if (sizeDirty || mPresentIntervalDirty)
363     {
364         resetSwapChain(clientWidth, clientHeight);
365         if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
366         {
367             glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
368         }
369
370         return true;
371     }
372     return false;
373 }
374
375 DWORD Surface::convertInterval(EGLint interval)
376 {
377     switch(interval)
378     {
379       case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
380       case 1: return D3DPRESENT_INTERVAL_ONE;
381       case 2: return D3DPRESENT_INTERVAL_TWO;
382       case 3: return D3DPRESENT_INTERVAL_THREE;
383       case 4: return D3DPRESENT_INTERVAL_FOUR;
384       default: UNREACHABLE();
385     }
386
387     return D3DPRESENT_INTERVAL_DEFAULT;
388 }
389
390 bool Surface::swap()
391 {
392     if (mSwapChain)
393     {
394         mDisplay->endScene();
395
396         HRESULT result = mSwapChain->Present(NULL, NULL, NULL, NULL, 0);
397
398         if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
399         {
400             return error(EGL_BAD_ALLOC, false);
401         }
402
403         if (result == D3DERR_DEVICELOST || result == D3DERR_DEVICEHUNG || result == D3DERR_DEVICEREMOVED)
404         {
405             return error(EGL_CONTEXT_LOST, false);
406         }
407
408         ASSERT(SUCCEEDED(result));
409
410         checkForOutOfDateSwapChain();
411     }
412
413     return true;
414 }
415
416 EGLint Surface::getWidth() const
417 {
418     return mWidth;
419 }
420
421 EGLint Surface::getHeight() const
422 {
423     return mHeight;
424 }
425
426 IDirect3DSurface9 *Surface::getRenderTarget()
427 {
428     if (mRenderTarget)
429     {
430         mRenderTarget->AddRef();
431     }
432
433     return mRenderTarget;
434 }
435
436 IDirect3DSurface9 *Surface::getDepthStencil()
437 {
438     if (mDepthStencil)
439     {
440         mDepthStencil->AddRef();
441     }
442
443     return mDepthStencil;
444 }
445
446 IDirect3DTexture9 *Surface::getOffscreenTexture()
447 {
448     if (mOffscreenTexture)
449     {
450         mOffscreenTexture->AddRef();
451     }
452
453     return mOffscreenTexture;
454 }
455
456 void Surface::setSwapInterval(EGLint interval)
457 {
458     if (mSwapInterval == interval)
459     {
460         return;
461     }
462     
463     mSwapInterval = interval;
464     mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval());
465     mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval());
466
467     mPresentInterval = convertInterval(mSwapInterval);
468     mPresentIntervalDirty = true;
469 }
470
471 EGLenum Surface::getTextureFormat() const
472 {
473     return mTextureFormat;
474 }
475
476 EGLenum Surface::getTextureTarget() const
477 {
478     return mTextureTarget;
479 }
480
481 void Surface::setBoundTexture(gl::Texture2D *texture)
482 {
483     mTexture = texture;
484 }
485
486 gl::Texture2D *Surface::getBoundTexture() const
487 {
488     return mTexture;
489 }
490
491 D3DFORMAT Surface::getFormat() const
492 {
493     return mConfig->mRenderTargetFormat;
494 }
495 }