2010-11-11 Alejandro G. Castro <alex@igalia.com>
[WebKit-https.git] / 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 "libEGL/Surface.h"
12
13 #include "common/debug.h"
14
15 #include "libEGL/main.h"
16 #include "libEGL/Display.h"
17
18 namespace egl
19 {
20 Surface::Surface(Display *display, const Config *config, HWND window) 
21     : mDisplay(display), mConfig(config), mWindow(window)
22 {
23     mSwapChain = NULL;
24     mDepthStencil = NULL;
25     mBackBuffer = NULL;
26     mRenderTarget = NULL;
27     mFlipTexture = NULL;
28     mFlipState = NULL;
29     mPreFlipState = NULL;
30
31     mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
32     mRenderBuffer = EGL_BACK_BUFFER;
33     mSwapBehavior = EGL_BUFFER_PRESERVED;
34
35     resetSwapChain();
36 }
37
38 Surface::~Surface()
39 {
40     if (mSwapChain)
41     {
42         mSwapChain->Release();
43     }
44
45     if (mBackBuffer)
46     {
47         mBackBuffer->Release();
48     }
49
50     if (mRenderTarget)
51     {
52         mRenderTarget->Release();
53     }
54
55     if (mDepthStencil)
56     {
57         mDepthStencil->Release();
58     }
59
60     if (mFlipTexture)
61     {
62         mFlipTexture->Release();
63     }
64
65     if (mFlipState)
66     {
67         mFlipState->Release();
68     }
69
70     if (mPreFlipState)
71     {
72         mPreFlipState->Release();
73     }
74 }
75
76 void Surface::resetSwapChain()
77 {
78     IDirect3DDevice9 *device = mDisplay->getDevice();
79
80     D3DPRESENT_PARAMETERS presentParameters = {0};
81
82     presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat;
83     presentParameters.BackBufferCount = 1;
84     presentParameters.BackBufferFormat = mConfig->mRenderTargetFormat;
85     presentParameters.EnableAutoDepthStencil = FALSE;
86     presentParameters.Flags = 0;
87     presentParameters.hDeviceWindow = getWindowHandle();
88     presentParameters.MultiSampleQuality = 0;                  // FIXME: Unimplemented
89     presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;   // FIXME: Unimplemented
90     presentParameters.PresentationInterval = Display::convertInterval(mConfig->mMinSwapInterval);
91     presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
92     presentParameters.Windowed = TRUE;
93
94     RECT windowRect;
95     if (!GetClientRect(getWindowHandle(), &windowRect))
96     {
97         ASSERT(false);
98         return;
99     }
100
101     presentParameters.BackBufferWidth = windowRect.right - windowRect.left;
102     presentParameters.BackBufferHeight = windowRect.bottom - windowRect.top;
103
104     IDirect3DSwapChain9 *swapChain = NULL;
105     HRESULT result = device->CreateAdditionalSwapChain(&presentParameters, &swapChain);
106
107     if (FAILED(result))
108     {
109         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
110
111         ERR("Could not create additional swap chains: %08lX", result);
112         return error(EGL_BAD_ALLOC);
113     }
114
115     IDirect3DSurface9 *depthStencilSurface = NULL;
116     result = device->CreateDepthStencilSurface(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight,
117                                                presentParameters.AutoDepthStencilFormat, presentParameters.MultiSampleType,
118                                                presentParameters.MultiSampleQuality, FALSE, &depthStencilSurface, NULL);
119
120     if (FAILED(result))
121     {
122         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
123
124         swapChain->Release();
125
126         ERR("Could not create depthstencil surface for new swap chain: %08lX", result);
127         return error(EGL_BAD_ALLOC);
128     }
129
130     IDirect3DSurface9 *renderTarget = NULL;
131     result = device->CreateRenderTarget(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, presentParameters.BackBufferFormat,
132                                         presentParameters.MultiSampleType, presentParameters.MultiSampleQuality, FALSE, &renderTarget, NULL);
133
134     if (FAILED(result))
135     {
136         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
137
138         swapChain->Release();
139         depthStencilSurface->Release();
140
141         ERR("Could not create render target surface for new swap chain: %08lX", result);
142         return error(EGL_BAD_ALLOC);
143     }
144
145     ASSERT(SUCCEEDED(result));
146
147     IDirect3DTexture9 *flipTexture = NULL;
148     result = device->CreateTexture(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 1, D3DUSAGE_RENDERTARGET,
149                                    presentParameters.BackBufferFormat, D3DPOOL_DEFAULT, &flipTexture, NULL);
150
151     if (FAILED(result))
152     {
153         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
154
155         swapChain->Release();
156         depthStencilSurface->Release();
157         renderTarget->Release();
158
159         ERR("Could not create flip texture for new swap chain: %08lX", result);
160         return error(EGL_BAD_ALLOC);
161     }
162
163     IDirect3DSurface9 *backBuffer = NULL;
164     swapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backBuffer);
165
166     if (mSwapChain) mSwapChain->Release();
167     if (mDepthStencil) mDepthStencil->Release();
168     if (mBackBuffer) mBackBuffer->Release();
169     if (mRenderTarget) mRenderTarget->Release();
170     if (mFlipTexture) mFlipTexture->Release();
171
172     mWidth = presentParameters.BackBufferWidth;
173     mHeight = presentParameters.BackBufferHeight;
174
175     mSwapChain = swapChain;
176     mDepthStencil = depthStencilSurface;
177     mBackBuffer = backBuffer;
178     mRenderTarget = renderTarget;
179     mFlipTexture = flipTexture;
180
181     // The flip state block recorded mFlipTexture so it is now invalid.
182     releaseRecordedState(device);
183 }
184
185 HWND Surface::getWindowHandle()
186 {
187     return mWindow;
188 }
189
190 void Surface::writeRecordableFlipState(IDirect3DDevice9 *device)
191 {
192     // Disable all pipeline operations
193     device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
194     device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
195     device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
196     device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
197     device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
198     device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
199     device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
200     device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
201     device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
202     device->SetPixelShader(NULL);
203     device->SetVertexShader(NULL);
204
205     // Just sample the texture
206     device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
207     device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
208     device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
209     device->SetTexture(0, NULL); // The actual texture will change after resizing. But the pre-flip state block must save/restore the texture.
210     device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
211     device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
212     device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
213     device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
214     device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
215     device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
216
217     device->SetStreamSourceFreq(0, 1); // DrawPrimitiveUP only cares about stream 0, not the rest.
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     mPreFlipState->Apply();
279
280     device->SetRenderTarget(0, mPreFlipBackBuffer);
281     device->SetDepthStencilSurface(mPreFlipDepthStencil);
282
283     if (mPreFlipBackBuffer)
284     {
285         mPreFlipBackBuffer->Release();
286         mPreFlipBackBuffer = NULL;
287     }
288
289     if (mPreFlipDepthStencil)
290     {
291         mPreFlipDepthStencil->Release();
292         mPreFlipDepthStencil = NULL;
293     }
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
313 bool Surface::checkForWindowResize()
314 {
315     RECT client;
316     if (!GetClientRect(getWindowHandle(), &client))
317     {
318         ASSERT(false);
319         return false;
320     }
321
322     if (getWidth() != client.right - client.left || getHeight() != client.bottom - client.top)
323     {
324         resetSwapChain();
325
326         if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
327         {
328             glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
329         }
330
331         return true;
332     }
333
334     return false;
335 }
336
337 bool Surface::swap()
338 {
339     if (mSwapChain)
340     {
341         IDirect3DTexture9 *flipTexture = mFlipTexture;
342         flipTexture->AddRef();
343
344         IDirect3DSurface9 *renderTarget = mRenderTarget;
345         renderTarget->AddRef();
346
347         EGLint oldWidth = mWidth;
348         EGLint oldHeight = mHeight;
349
350         checkForWindowResize();
351
352         IDirect3DDevice9 *device = mDisplay->getDevice();
353
354         IDirect3DSurface9 *textureSurface;
355         flipTexture->GetSurfaceLevel(0, &textureSurface);
356
357         mDisplay->endScene();
358         device->StretchRect(renderTarget, NULL, textureSurface, NULL, D3DTEXF_NONE);
359         renderTarget->Release();
360
361         applyFlipState(device);
362         device->SetTexture(0, flipTexture);
363
364         float xscale = (float)mWidth / oldWidth;
365         float yscale = (float)mHeight / oldHeight;
366
367         // Render the texture upside down into the back buffer
368         // Texcoords are chosen to pin a potentially resized image into the upper-left corner without scaling.
369         float quad[4][6] = {{     0 - 0.5f,       0 - 0.5f, 0.0f, 1.0f, 0.0f,   1.0f       },
370                             {mWidth - 0.5f,       0 - 0.5f, 0.0f, 1.0f, xscale, 1.0f       },
371                             {mWidth - 0.5f, mHeight - 0.5f, 0.0f, 1.0f, xscale, 1.0f-yscale},
372                             {     0 - 0.5f, mHeight - 0.5f, 0.0f, 1.0f, 0.0f,   1.0f-yscale}};   // x, y, z, rhw, u, v
373
374         mDisplay->startScene();
375         device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
376
377         flipTexture->Release();
378         textureSurface->Release();
379
380         restoreState(device);
381
382         mDisplay->endScene();
383         HRESULT result = mSwapChain->Present(NULL, NULL, NULL, NULL, mDisplay->getPresentInterval());
384
385         if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
386         {
387             return error(EGL_BAD_ALLOC, false);
388         }
389
390         if (result == D3DERR_DEVICELOST)
391         {
392             return error(EGL_CONTEXT_LOST, false);
393         }
394
395         ASSERT(SUCCEEDED(result));
396
397     }
398
399     return true;
400 }
401
402 EGLint Surface::getWidth() const
403 {
404     return mWidth;
405 }
406
407 EGLint Surface::getHeight() const
408 {
409     return mHeight;
410 }
411
412 IDirect3DSurface9 *Surface::getRenderTarget()
413 {
414     if (mRenderTarget)
415     {
416         mRenderTarget->AddRef();
417     }
418
419     return mRenderTarget;
420 }
421
422 IDirect3DSurface9 *Surface::getDepthStencil()
423 {
424     if (mDepthStencil)
425     {
426         mDepthStencil->AddRef();
427     }
428
429     return mDepthStencil;
430 }
431 }