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 {
Surface(Display * display,const Config * config,HWND window)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
~Surface()42 Surface::~Surface()
43 {
44 unsubclassWindow();
45 release();
46 }
47
release()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
resetSwapChain()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
resetSwapChain(int backbufferWidth,int backbufferHeight)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
getWindowHandle()181 HWND Surface::getWindowHandle()
182 {
183 return mWindow;
184 }
185
writeRecordableFlipState(IDirect3DDevice9 * device)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
applyFlipState(IDirect3DDevice9 * device)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
restoreState(IDirect3DDevice9 * device)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.
releaseRecordedState(IDirect3DDevice9 * device)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
SurfaceWindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)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
subclassWindow()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
unsubclassWindow()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
checkForOutOfDateSwapChain()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
convertInterval(EGLint interval)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
swap()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
getWidth() const451 EGLint Surface::getWidth() const
452 {
453 return mWidth;
454 }
455
getHeight() const456 EGLint Surface::getHeight() const
457 {
458 return mHeight;
459 }
460
getRenderTarget()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
getDepthStencil()473 IDirect3DSurface9 *Surface::getDepthStencil()
474 {
475 if (mDepthStencil)
476 {
477 mDepthStencil->AddRef();
478 }
479
480 return mDepthStencil;
481 }
482
setSwapInterval(EGLint interval)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 }
498