Part 2 of 2: <http://webkit.org/b/56337> Enable -Werror on ANGLE
[WebKit-https.git] / 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
17 #include "libEGL/main.h"
18 #include "libEGL/Display.h"
19
20 namespace egl
21 {
22 Surface::Surface(Display *display, const Config *config, HWND window) 
23     : mDisplay(display), mConfig(config), mWindow(window)
24 {
25     mSwapChain = NULL;
26     mDepthStencil = NULL;
27     mBackBuffer = NULL;
28     mFlipTexture = NULL;
29     mFlipState = NULL;
30     mPreFlipState = NULL;
31
32     mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
33     mRenderBuffer = EGL_BACK_BUFFER;
34     mSwapBehavior = EGL_BUFFER_PRESERVED;
35     mSwapInterval = -1;
36     setSwapInterval(1);
37
38     subclassWindow();
39     resetSwapChain();
40 }
41
42 Surface::~Surface()
43 {
44     unsubclassWindow();
45     release();
46 }
47
48 void Surface::release()
49 {
50     if (mSwapChain)
51     {
52         mSwapChain->Release();
53         mSwapChain = NULL;
54     }
55
56     if (mBackBuffer)
57     {
58         mBackBuffer->Release();
59         mBackBuffer = NULL;
60     }
61
62     if (mDepthStencil)
63     {
64         mDepthStencil->Release();
65         mDepthStencil = NULL;
66     }
67
68     if (mFlipTexture)
69     {
70         mFlipTexture->Release();
71         mFlipTexture = NULL;
72     }
73
74     if (mFlipState)
75     {
76         mFlipState->Release();
77         mFlipState = NULL;
78     }
79
80     if (mPreFlipState)
81     {
82         mPreFlipState->Release();
83         mPreFlipState = NULL;
84     }
85 }
86
87 void Surface::resetSwapChain()
88 {
89     RECT windowRect;
90     if (!GetClientRect(getWindowHandle(), &windowRect))
91     {
92         ASSERT(false);
93
94         ERR("Could not retrieve the window dimensions");
95         return;
96     }
97
98     resetSwapChain(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
99 }
100
101 void Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
102 {
103     IDirect3DDevice9 *device = mDisplay->getDevice();
104
105     if (device == NULL)
106     {
107         return;
108     }
109
110     // Evict all non-render target textures to system memory and release all resources
111     // before reallocating them to free up as much video memory as possible.
112     device->EvictManagedResources();
113     release();
114     
115     D3DPRESENT_PARAMETERS presentParameters = {0};
116
117     presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat;
118     presentParameters.BackBufferCount = 1;
119     presentParameters.BackBufferFormat = mConfig->mRenderTargetFormat;
120     presentParameters.EnableAutoDepthStencil = FALSE;
121     presentParameters.Flags = 0;
122     presentParameters.hDeviceWindow = getWindowHandle();
123     presentParameters.MultiSampleQuality = 0;                  // FIXME: Unimplemented
124     presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;   // FIXME: Unimplemented
125     presentParameters.PresentationInterval = mPresentInterval;
126     presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
127     presentParameters.Windowed = TRUE;
128     presentParameters.BackBufferWidth = backbufferWidth;
129     presentParameters.BackBufferHeight = backbufferHeight;
130
131     HRESULT result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
132
133     if (FAILED(result))
134     {
135         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
136
137         ERR("Could not create additional swap chains: %08lX", result);
138         release();
139         return error(EGL_BAD_ALLOC);
140     }
141
142     result = device->CreateDepthStencilSurface(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight,
143                                                presentParameters.AutoDepthStencilFormat, presentParameters.MultiSampleType,
144                                                presentParameters.MultiSampleQuality, FALSE, &mDepthStencil, NULL);
145
146     if (FAILED(result))
147     {
148         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
149
150         ERR("Could not create depthstencil surface for new swap chain: %08lX", result);
151         release();
152         return error(EGL_BAD_ALLOC);
153     }
154
155     ASSERT(SUCCEEDED(result));
156
157     result = device->CreateTexture(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 1, D3DUSAGE_RENDERTARGET,
158                                    presentParameters.BackBufferFormat, D3DPOOL_DEFAULT, &mFlipTexture, NULL);
159
160     if (FAILED(result))
161     {
162         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
163
164         ERR("Could not create flip texture for new swap chain: %08lX", result);
165         release();
166         return error(EGL_BAD_ALLOC);
167     }
168
169     mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
170     mWidth = presentParameters.BackBufferWidth;
171     mHeight = presentParameters.BackBufferHeight;
172
173     mPresentIntervalDirty = false;
174
175     InvalidateRect(mWindow, NULL, FALSE);
176
177     // The flip state block recorded mFlipTexture so it is now invalid.
178     releaseRecordedState(device);
179 }
180
181 HWND Surface::getWindowHandle()
182 {
183     return mWindow;
184 }
185
186 void Surface::writeRecordableFlipState(IDirect3DDevice9 *device)
187 {
188     // Disable all pipeline operations
189     device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
190     device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
191     device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
192     device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
193     device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
194     device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
195     device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
196     device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
197     device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
198     device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
199     device->SetPixelShader(NULL);
200     device->SetVertexShader(NULL);
201
202     // Just sample the texture
203     device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
204     device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
205     device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
206     device->SetTexture(0, NULL);   // The actual texture will change after resizing. But the pre-flip state block must save/restore the texture.
207     device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
208     device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
209     device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
210     device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
211     device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
212     device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
213
214     RECT scissorRect = {0};   // Scissoring is disabled for flipping, but we need this to capture and restore the old rectangle
215     device->SetScissorRect(&scissorRect);
216     D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f};
217     device->SetViewport(&viewport);
218 }
219
220 void Surface::applyFlipState(IDirect3DDevice9 *device)
221 {
222     HRESULT hr;
223
224     if (mFlipState == NULL)
225     {
226         // Create two state blocks both recording the states that are changed when swapping.
227
228         // mPreFlipState will record the original state each entry.
229         hr = device->BeginStateBlock();
230         ASSERT(SUCCEEDED(hr));
231         writeRecordableFlipState(device);
232         hr = device->EndStateBlock(&mPreFlipState);
233         ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
234
235         if (SUCCEEDED(hr))
236         {
237             mPreFlipState->Capture();
238         }
239
240         // mFlipState will record the state for the swap operation.
241         hr = device->BeginStateBlock();
242         ASSERT(SUCCEEDED(hr));
243
244         writeRecordableFlipState(device);
245
246         hr = device->EndStateBlock(&mFlipState);
247         ASSERT(SUCCEEDED(hr) || hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY);
248
249         if (FAILED(hr))
250         {
251             mFlipState = NULL;
252             mPreFlipState->Release();
253             mPreFlipState = NULL;
254         }
255         else
256         {
257             hr = mFlipState->Apply();
258             ASSERT(SUCCEEDED(hr));
259         }
260     }
261     else
262     {
263         hr = mPreFlipState->Capture();
264         ASSERT(SUCCEEDED(hr));
265         hr = mFlipState->Apply();
266         ASSERT(SUCCEEDED(hr));
267     }
268
269     device->GetRenderTarget(0, &mPreFlipBackBuffer);
270     device->GetDepthStencilSurface(&mPreFlipDepthStencil);
271
272     device->SetRenderTarget(0, mBackBuffer);
273     device->SetDepthStencilSurface(NULL);
274 }
275
276 void Surface::restoreState(IDirect3DDevice9 *device)
277 {
278     device->SetRenderTarget(0, mPreFlipBackBuffer);
279     device->SetDepthStencilSurface(mPreFlipDepthStencil);
280
281     if (mPreFlipBackBuffer)
282     {
283         mPreFlipBackBuffer->Release();
284         mPreFlipBackBuffer = NULL;
285     }
286
287     if (mPreFlipDepthStencil)
288     {
289         mPreFlipDepthStencil->Release();
290         mPreFlipDepthStencil = NULL;
291     }
292
293     mPreFlipState->Apply();
294 }
295
296 // On the next flip, this will cause the state to be recorded from scratch.
297 // In particular we need to do this if the flip texture changes.
298 void Surface::releaseRecordedState(IDirect3DDevice9 *device)
299 {
300     if (mFlipState)
301     {
302         mFlipState->Release();
303         mFlipState = NULL;
304     }
305
306     if (mPreFlipState)
307     {
308         mPreFlipState->Release();
309         mPreFlipState = NULL;
310     }
311 }
312 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
313 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
314
315 static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
316   if (message == WM_SIZE) {
317       Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
318       if(surf) {
319         surf->checkForOutOfDateSwapChain();
320       }
321   }
322   WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
323   return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
324 }
325
326 void Surface::subclassWindow()
327 {
328   SetLastError(0);
329   LONG oldWndProc = SetWindowLong(mWindow, GWL_WNDPROC, reinterpret_cast<LONG>(SurfaceWindowProc));
330   if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS) {
331     mWindowSubclassed = false;
332     return;
333   }
334
335   SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
336   SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
337   mWindowSubclassed = true;
338 }
339
340 void Surface::unsubclassWindow()
341 {
342   if(!mWindowSubclassed)
343     return;
344
345   // un-subclass
346   LONG parentWndFunc = reinterpret_cast<LONG>(GetProp(mWindow, kParentWndProc));
347
348   // Check the windowproc is still SurfaceWindowProc.
349   // If this assert fails, then it is likely the application has subclassed the
350   // hwnd as well and did not unsubclass before destroying its EGL context. The
351   // application should be modified to either subclass before initializing the
352   // EGL context, or to unsubclass before destroying the EGL context.
353   if(parentWndFunc) {
354     LONG prevWndFunc = SetWindowLong(mWindow, GWL_WNDPROC, parentWndFunc);
355     ASSERT(prevWndFunc == reinterpret_cast<LONG>(SurfaceWindowProc));
356   }
357
358   RemoveProp(mWindow, kSurfaceProperty);
359   RemoveProp(mWindow, kParentWndProc);
360   mWindowSubclassed = false;
361 }
362
363 bool Surface::checkForOutOfDateSwapChain()
364 {
365     RECT client;
366     if (!GetClientRect(getWindowHandle(), &client))
367     {
368         ASSERT(false);
369         return false;
370     }
371
372     // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
373     int clientWidth = client.right - client.left;
374     int clientHeight = client.bottom - client.top;
375     bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
376
377     if (sizeDirty || mPresentIntervalDirty)
378     {
379         resetSwapChain(clientWidth, clientHeight);
380         if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
381         {
382             glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
383         }
384
385         return true;
386     }
387     return false;
388 }
389
390 DWORD Surface::convertInterval(EGLint interval)
391 {
392     switch(interval)
393     {
394       case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
395       case 1: return D3DPRESENT_INTERVAL_ONE;
396       case 2: return D3DPRESENT_INTERVAL_TWO;
397       case 3: return D3DPRESENT_INTERVAL_THREE;
398       case 4: return D3DPRESENT_INTERVAL_FOUR;
399       default: UNREACHABLE();
400     }
401
402     return D3DPRESENT_INTERVAL_DEFAULT;
403 }
404
405
406 bool Surface::swap()
407 {
408     if (mSwapChain)
409     {
410         IDirect3DDevice9 *device = mDisplay->getDevice();
411
412         applyFlipState(device);
413         device->SetTexture(0, mFlipTexture);
414
415         // Render the texture upside down into the back buffer
416         // Texcoords are chosen to flip the renderTarget about its Y axis.
417         float w = static_cast<float>(getWidth());
418         float h = static_cast<float>(getHeight());
419         float quad[4][6] = {{0 - 0.5f, 0 - 0.5f, 0.0f, 1.0f, 0.0f, 1.0f},
420                             {w - 0.5f, 0 - 0.5f, 0.0f, 1.0f, 1.0f, 1.0f},
421                             {w - 0.5f, h - 0.5f, 0.0f, 1.0f, 1.0f, 0.0f},
422                             {0 - 0.5f, h - 0.5f, 0.0f, 1.0f, 0.0f, 0.0f}};   // x, y, z, rhw, u, v
423
424         mDisplay->startScene();
425         device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
426
427         restoreState(device);
428
429         mDisplay->endScene();
430
431         HRESULT result = mSwapChain->Present(NULL, NULL, NULL, NULL, 0);
432
433         if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
434         {
435             return error(EGL_BAD_ALLOC, false);
436         }
437
438         if (result == D3DERR_DEVICELOST)
439         {
440             return error(EGL_CONTEXT_LOST, false);
441         }
442
443         ASSERT(SUCCEEDED(result));
444
445         checkForOutOfDateSwapChain();
446     }
447
448     return true;
449 }
450
451 EGLint Surface::getWidth() const
452 {
453     return mWidth;
454 }
455
456 EGLint Surface::getHeight() const
457 {
458     return mHeight;
459 }
460
461 IDirect3DSurface9 *Surface::getRenderTarget()
462 {
463     IDirect3DSurface9 *textureSurface = NULL;
464
465     if (mFlipTexture)
466     {
467         mFlipTexture->GetSurfaceLevel(0, &textureSurface);
468     }
469
470     return textureSurface;
471 }
472
473 IDirect3DSurface9 *Surface::getDepthStencil()
474 {
475     if (mDepthStencil)
476     {
477         mDepthStencil->AddRef();
478     }
479
480     return mDepthStencil;
481 }
482
483 void Surface::setSwapInterval(EGLint interval)
484 {
485     if (mSwapInterval == interval)
486     {
487         return;
488     }
489     
490     mSwapInterval = interval;
491     mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval());
492     mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval());
493
494     mPresentInterval = convertInterval(mSwapInterval);
495     mPresentIntervalDirty = true;
496 }
497 }