732c404b10bfb80fc76ab14ef18a6cb4359f34ad
[profile/ivi/qtbase.git] / src / 3rdparty / 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 #include "libGLESv2/Texture.h"
17
18 #include "libEGL/main.h"
19 #include "libEGL/Display.h"
20
21 #include <dwmapi.h>
22
23 namespace egl
24 {
25
26 Surface::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
48 Surface::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
68 Surface::~Surface()
69 {
70     unsubclassWindow();
71     release();
72 }
73
74 bool 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
102 void 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
143 bool 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
162 bool 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
175     HRESULT result;
176
177     // 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
210     result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
211                                    mConfig->mRenderTargetFormat, D3DPOOL_DEFAULT, &mOffscreenTexture, pShareHandle);
212     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
241         if (rect.right > static_cast<LONG>(backbufferWidth))
242         {
243             rect.right = backbufferWidth;
244         }
245
246         if (rect.bottom > static_cast<LONG>(backbufferHeight))
247         {
248             rect.bottom = backbufferHeight;
249         }
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     {
261         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
277         // 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         }
289
290         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     {
316         result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight, mConfig->mDepthStencilFormat, D3DMULTISAMPLE_NONE,
317                                                    0, FALSE, &mDepthStencil, NULL);
318
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
338     mWidth = backbufferWidth;
339     mHeight = backbufferHeight;
340
341     mPresentIntervalDirty = false;
342     return true;
343 }
344
345 bool 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
452 HWND Surface::getWindowHandle()
453 {
454     return mWindow;
455 }
456
457
458 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
459 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
460
461 static 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
475 void 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
502 void 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
528 bool 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
555 DWORD 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
570 bool Surface::swap()
571 {
572     return swapRect(0, 0, mWidth, mHeight);
573 }
574
575 bool 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
586 EGLint Surface::getWidth() const
587 {
588     return mWidth;
589 }
590
591 EGLint Surface::getHeight() const
592 {
593     return mHeight;
594 }
595
596 EGLint Surface::isPostSubBufferSupported() const
597 {
598     return mPostSubBufferSupported;
599 }
600
601 // Increments refcount on surface.
602 // caller must Release() the returned surface
603 IDirect3DSurface9 *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
615 IDirect3DSurface9 *Surface::getDepthStencil()
616 {
617     if (mDepthStencil)
618     {
619         mDepthStencil->AddRef();
620     }
621
622     return mDepthStencil;
623 }
624
625 IDirect3DTexture9 *Surface::getOffscreenTexture()
626 {
627     if (mOffscreenTexture)
628     {
629         mOffscreenTexture->AddRef();
630     }
631
632     return mOffscreenTexture;
633 }
634
635 void 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
650 EGLenum Surface::getTextureFormat() const
651 {
652     return mTextureFormat;
653 }
654
655 EGLenum Surface::getTextureTarget() const
656 {
657     return mTextureTarget;
658 }
659
660 void Surface::setBoundTexture(gl::Texture2D *texture)
661 {
662     mTexture = texture;
663 }
664
665 gl::Texture2D *Surface::getBoundTexture() const
666 {
667     return mTexture;
668 }
669
670 D3DFORMAT Surface::getFormat() const
671 {
672     return mConfig->mRenderTargetFormat;
673 }
674 }