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