blob: 01491cbe22667f72663a6d441ce6832acc72571e [file] [log] [blame]
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001//
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
23namespace egl
24{
25
26namespace
27{
28const int versionWindowsVista = MAKEWORD(0x00, 0x06);
29const int versionWindows7 = MAKEWORD(0x01, 0x06);
30
31// Return the version of the operating system in a format suitable for ordering
32// comparison.
33int 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
42Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported)
43 : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported)
44{
45 mSwapChain = NULL;
46 mBackBuffer = NULL;
47 mDepthStencil = NULL;
48 mRenderTarget = NULL;
49 mOffscreenTexture = NULL;
50 mShareHandle = NULL;
51 mTexture = NULL;
52 mTextureFormat = EGL_NO_TEXTURE;
53 mTextureTarget = EGL_NO_TEXTURE;
54
55 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
56 mRenderBuffer = EGL_BACK_BUFFER;
57 mSwapBehavior = EGL_BUFFER_PRESERVED;
58 mSwapInterval = -1;
59 setSwapInterval(1);
60
61 subclassWindow();
62}
63
64Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
65 : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE)
66{
67 mSwapChain = NULL;
68 mBackBuffer = NULL;
69 mDepthStencil = NULL;
70 mRenderTarget = NULL;
71 mOffscreenTexture = NULL;
72 mWindowSubclassed = false;
73 mTexture = NULL;
74 mTextureFormat = textureFormat;
75 mTextureTarget = textureType;
76
77 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
78 mRenderBuffer = EGL_BACK_BUFFER;
79 mSwapBehavior = EGL_BUFFER_PRESERVED;
80 mSwapInterval = -1;
81 setSwapInterval(1);
82}
83
84Surface::~Surface()
85{
86 unsubclassWindow();
87 release();
88}
89
90bool Surface::initialize()
91{
92 ASSERT(!mSwapChain && !mOffscreenTexture && !mDepthStencil);
93
94 if (!resetSwapChain())
95 return false;
96
97 // Modify present parameters for this window, if we are composited,
98 // to minimize the amount of queuing done by DWM between our calls to
99 // present and the actual screen.
100 if (mWindow && (getComparableOSVersion() >= versionWindowsVista)) {
101 BOOL isComposited;
102 HRESULT result = DwmIsCompositionEnabled(&isComposited);
103 if (SUCCEEDED(result) && isComposited) {
104 DWM_PRESENT_PARAMETERS presentParams;
105 memset(&presentParams, 0, sizeof(presentParams));
106 presentParams.cbSize = sizeof(DWM_PRESENT_PARAMETERS);
107 presentParams.cBuffer = 2;
108
109 result = DwmSetPresentParameters(mWindow, &presentParams);
110 if (FAILED(result))
111 ERR("Unable to set present parameters: 0x%08X", result);
112 }
113 }
114
115 return true;
116}
117
118void Surface::release()
119{
120 if (mSwapChain)
121 {
122 mSwapChain->Release();
123 mSwapChain = NULL;
124 }
125
126 if (mBackBuffer)
127 {
128 mBackBuffer->Release();
129 mBackBuffer = NULL;
130 }
131
132 if (mDepthStencil)
133 {
134 mDepthStencil->Release();
135 mDepthStencil = NULL;
136 }
137
138 if (mRenderTarget)
139 {
140 mRenderTarget->Release();
141 mRenderTarget = NULL;
142 }
143
144 if (mOffscreenTexture)
145 {
146 mOffscreenTexture->Release();
147 mOffscreenTexture = NULL;
148 }
149
150 if (mTexture)
151 {
152 mTexture->releaseTexImage();
153 mTexture = NULL;
154 }
155
156 mShareHandle = NULL;
157}
158
159bool Surface::resetSwapChain()
160{
161 if (!mWindow)
162 {
163 return resetSwapChain(mWidth, mHeight);
164 }
165
166 RECT windowRect;
167 if (!GetClientRect(getWindowHandle(), &windowRect))
168 {
169 ASSERT(false);
170
171 ERR("Could not retrieve the window dimensions");
172 return false;
173 }
174
175 return resetSwapChain(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
176}
177
178bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
179{
180 IDirect3DDevice9 *device = mDisplay->getDevice();
181
182 if (device == NULL)
183 {
184 return false;
185 }
186
187 // Evict all non-render target textures to system memory and release all resources
188 // before reallocating them to free up as much video memory as possible.
189 device->EvictManagedResources();
190
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000191 HRESULT result;
192
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000193 // Release specific resources to free up memory for the new render target, while the
194 // old render target still exists for the purpose of preserving its contents.
195 if (mSwapChain)
196 {
197 mSwapChain->Release();
198 mSwapChain = NULL;
199 }
200
201 if (mBackBuffer)
202 {
203 mBackBuffer->Release();
204 mBackBuffer = NULL;
205 }
206
207 if (mOffscreenTexture)
208 {
209 mOffscreenTexture->Release();
210 mOffscreenTexture = NULL;
211 }
212
213 if (mDepthStencil)
214 {
215 mDepthStencil->Release();
216 mDepthStencil = NULL;
217 }
218
219 mShareHandle = NULL;
220 HANDLE *pShareHandle = NULL;
221 if (!mWindow && mDisplay->shareHandleSupported())
222 {
223 pShareHandle = &mShareHandle;
224 }
225
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000226 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
227 mConfig->mRenderTargetFormat, D3DPOOL_DEFAULT, &mOffscreenTexture, pShareHandle);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000228 if (FAILED(result))
229 {
230 ERR("Could not create offscreen texture: %08lX", result);
231 release();
232
233 if(isDeviceLostError(result))
234 {
235 mDisplay->notifyDeviceLost();
236 return false;
237 }
238 else
239 {
240 return error(EGL_BAD_ALLOC, false);
241 }
242 }
243
244 IDirect3DSurface9 *oldRenderTarget = mRenderTarget;
245
246 result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
247 ASSERT(SUCCEEDED(result));
248
249 if (oldRenderTarget)
250 {
251 RECT rect =
252 {
253 0, 0,
254 mWidth, mHeight
255 };
256
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000257 if (rect.right > static_cast<LONG>(backbufferWidth))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000258 {
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000259 rect.right = backbufferWidth;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000260 }
261
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000262 if (rect.bottom > static_cast<LONG>(backbufferHeight))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000263 {
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000264 rect.bottom = backbufferHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000265 }
266
267 mDisplay->endScene();
268
269 result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE);
270 ASSERT(SUCCEEDED(result));
271
272 oldRenderTarget->Release();
273 }
274
275 if (mWindow)
276 {
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000277 D3DPRESENT_PARAMETERS presentParameters = {0};
278 presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat;
279 presentParameters.BackBufferCount = 1;
280 presentParameters.BackBufferFormat = mConfig->mRenderTargetFormat;
281 presentParameters.EnableAutoDepthStencil = FALSE;
282 presentParameters.Flags = 0;
283 presentParameters.hDeviceWindow = getWindowHandle();
284 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented
285 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented
286 presentParameters.PresentationInterval = mPresentInterval;
287 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
288 presentParameters.Windowed = TRUE;
289 presentParameters.BackBufferWidth = backbufferWidth;
290 presentParameters.BackBufferHeight = backbufferHeight;
291
292 // http://crbug.com/140239
293 // Some AMD GPUs / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width.
294 // This rounds the width up rather than down.
295 presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64;
296
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000297 result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
298
299 if (FAILED(result))
300 {
301 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST);
302
303 ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
304 release();
305
306 if(isDeviceLostError(result))
307 {
308 mDisplay->notifyDeviceLost();
309 return false;
310 }
311 else
312 {
313 return error(EGL_BAD_ALLOC, false);
314 }
315 }
316
317 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
318 ASSERT(SUCCEEDED(result));
319 }
320
321 if (mConfig->mDepthStencilFormat != D3DFMT_UNKNOWN)
322 {
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000323 result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight, mConfig->mDepthStencilFormat, D3DMULTISAMPLE_NONE,
324 0, FALSE, &mDepthStencil, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000325
326 if (FAILED(result))
327 {
328 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL);
329
330 ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result);
331 release();
332
333 if(isDeviceLostError(result))
334 {
335 mDisplay->notifyDeviceLost();
336 return false;
337 }
338 else
339 {
340 return error(EGL_BAD_ALLOC, false);
341 }
342 }
343 }
344
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000345 mWidth = backbufferWidth;
346 mHeight = backbufferHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000347
348 mPresentIntervalDirty = false;
349 return true;
350}
351
352bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
353{
354 if (!mSwapChain)
355 {
356 return true;
357 }
358
359 if (x + width > mWidth)
360 {
361 width = mWidth - x;
362 }
363
364 if (y + height > mHeight)
365 {
366 height = mHeight - y;
367 }
368
369 if (width == 0 || height == 0)
370 {
371 return true;
372 }
373
374 IDirect3DDevice9 *device = mDisplay->getDevice();
375
376 // Disable all pipeline operations
377 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
378 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
379 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
380 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
381 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
382 device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
383 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
384 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
385 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
386 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
387 device->SetPixelShader(NULL);
388 device->SetVertexShader(NULL);
389
390 device->SetRenderTarget(0, mBackBuffer);
391 device->SetDepthStencilSurface(NULL);
392
393 device->SetTexture(0, mOffscreenTexture);
394 device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
395 device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
396 device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
397 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
398 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
399 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
400 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
401 device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
402
403 D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f};
404 device->SetViewport(&viewport);
405
406 float x1 = x - 0.5f;
407 float y1 = (mHeight - y - height) - 0.5f;
408 float x2 = (x + width) - 0.5f;
409 float y2 = (mHeight - y) - 0.5f;
410
411 float u1 = x / float(mWidth);
412 float v1 = y / float(mHeight);
413 float u2 = (x + width) / float(mWidth);
414 float v2 = (y + height) / float(mHeight);
415
416 float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2},
417 {x2, y1, 0.0f, 1.0f, u2, v2},
418 {x2, y2, 0.0f, 1.0f, u2, v1},
419 {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v
420
421 mDisplay->startScene();
422 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
423 mDisplay->endScene();
424
425 device->SetTexture(0, NULL);
426
427 RECT rect =
428 {
429 x, mHeight - y - height,
430 x + width, mHeight - y
431 };
432
433 HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0);
434
435 gl::Context *context = static_cast<gl::Context*>(glGetCurrentContext());
436 if (context)
437 {
438 context->markAllStateDirty();
439 }
440
441 if (isDeviceLostError(result))
442 {
443 mDisplay->notifyDeviceLost();
444 return false;
445 }
446
447 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
448 {
449 return error(EGL_BAD_ALLOC, false);
450 }
451
452 ASSERT(SUCCEEDED(result));
453
454 checkForOutOfDateSwapChain();
455
456 return true;
457}
458
459HWND Surface::getWindowHandle()
460{
461 return mWindow;
462}
463
464
465#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
466#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
467
468static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
469{
470 if (message == WM_SIZE)
471 {
472 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
473 if(surf)
474 {
475 surf->checkForOutOfDateSwapChain();
476 }
477 }
478 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
479 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
480}
481
482void Surface::subclassWindow()
483{
484 if (!mWindow)
485 {
486 return;
487 }
488
489 DWORD processId;
490 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
491 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
492 {
493 return;
494 }
495
496 SetLastError(0);
497 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
498 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
499 {
500 mWindowSubclassed = false;
501 return;
502 }
503
504 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
505 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
506 mWindowSubclassed = true;
507}
508
509void Surface::unsubclassWindow()
510{
511 if(!mWindowSubclassed)
512 {
513 return;
514 }
515
516 // un-subclass
517 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
518
519 // Check the windowproc is still SurfaceWindowProc.
520 // If this assert fails, then it is likely the application has subclassed the
521 // hwnd as well and did not unsubclass before destroying its EGL context. The
522 // application should be modified to either subclass before initializing the
523 // EGL context, or to unsubclass before destroying the EGL context.
524 if(parentWndFunc)
525 {
526 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
527 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
528 }
529
530 RemoveProp(mWindow, kSurfaceProperty);
531 RemoveProp(mWindow, kParentWndProc);
532 mWindowSubclassed = false;
533}
534
535bool Surface::checkForOutOfDateSwapChain()
536{
537 RECT client;
538 if (!GetClientRect(getWindowHandle(), &client))
539 {
540 ASSERT(false);
541 return false;
542 }
543
544 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
545 int clientWidth = client.right - client.left;
546 int clientHeight = client.bottom - client.top;
547 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
548
549 if (sizeDirty || mPresentIntervalDirty)
550 {
551 resetSwapChain(clientWidth, clientHeight);
552 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
553 {
554 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
555 }
556
557 return true;
558 }
559 return false;
560}
561
562DWORD Surface::convertInterval(EGLint interval)
563{
564 switch(interval)
565 {
566 case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
567 case 1: return D3DPRESENT_INTERVAL_ONE;
568 case 2: return D3DPRESENT_INTERVAL_TWO;
569 case 3: return D3DPRESENT_INTERVAL_THREE;
570 case 4: return D3DPRESENT_INTERVAL_FOUR;
571 default: UNREACHABLE();
572 }
573
574 return D3DPRESENT_INTERVAL_DEFAULT;
575}
576
577bool Surface::swap()
578{
579 return swapRect(0, 0, mWidth, mHeight);
580}
581
582bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
583{
584 if (!mPostSubBufferSupported)
585 {
586 // Spec is not clear about how this should be handled.
587 return true;
588 }
589
590 return swapRect(x, y, width, height);
591}
592
593EGLint Surface::getWidth() const
594{
595 return mWidth;
596}
597
598EGLint Surface::getHeight() const
599{
600 return mHeight;
601}
602
603EGLint Surface::isPostSubBufferSupported() const
604{
605 return mPostSubBufferSupported;
606}
607
608// Increments refcount on surface.
609// caller must Release() the returned surface
610IDirect3DSurface9 *Surface::getRenderTarget()
611{
612 if (mRenderTarget)
613 {
614 mRenderTarget->AddRef();
615 }
616
617 return mRenderTarget;
618}
619
620// Increments refcount on surface.
621// caller must Release() the returned surface
622IDirect3DSurface9 *Surface::getDepthStencil()
623{
624 if (mDepthStencil)
625 {
626 mDepthStencil->AddRef();
627 }
628
629 return mDepthStencil;
630}
631
632IDirect3DTexture9 *Surface::getOffscreenTexture()
633{
634 if (mOffscreenTexture)
635 {
636 mOffscreenTexture->AddRef();
637 }
638
639 return mOffscreenTexture;
640}
641
642void Surface::setSwapInterval(EGLint interval)
643{
644 if (mSwapInterval == interval)
645 {
646 return;
647 }
648
649 mSwapInterval = interval;
650 mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval());
651 mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval());
652
653 mPresentInterval = convertInterval(mSwapInterval);
654 mPresentIntervalDirty = true;
655}
656
657EGLenum Surface::getTextureFormat() const
658{
659 return mTextureFormat;
660}
661
662EGLenum Surface::getTextureTarget() const
663{
664 return mTextureTarget;
665}
666
667void Surface::setBoundTexture(gl::Texture2D *texture)
668{
669 mTexture = texture;
670}
671
672gl::Texture2D *Surface::getBoundTexture() const
673{
674 return mTexture;
675}
676
677D3DFORMAT Surface::getFormat() const
678{
679 return mConfig->mRenderTargetFormat;
680}
681}