2010-11-11 Alejandro G. Castro <alex@igalia.com>
[WebKit-https.git] / ANGLE / src / libEGL / Display.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 // Display.cpp: Implements the egl::Display class, representing the abstract
8 // display on which graphics are drawn. Implements EGLDisplay.
9 // [EGL 1.4] section 2.1.2 page 3.
10
11 #include "libEGL/Display.h"
12
13 #include <algorithm>
14 #include <vector>
15
16 #include "common/debug.h"
17
18 #include "libEGL/main.h"
19
20 #define REF_RAST 0   // Can also be enabled by defining FORCE_REF_RAST in the project's predefined macros
21
22 namespace egl
23 {
24 Display::Display(HDC deviceContext) : mDc(deviceContext)
25 {
26     mD3d9Module = NULL;
27     
28     mD3d9 = NULL;
29     mD3d9ex = NULL;
30     mDevice = NULL;
31     mDeviceWindow = NULL;
32
33     mAdapter = D3DADAPTER_DEFAULT;
34
35     #if REF_RAST == 1 || defined(FORCE_REF_RAST)
36         mDeviceType = D3DDEVTYPE_REF;
37     #else
38         mDeviceType = D3DDEVTYPE_HAL;
39     #endif
40
41     mMinSwapInterval = 1;
42     mMaxSwapInterval = 1;
43     setSwapInterval(1);
44 }
45
46 Display::~Display()
47 {
48     terminate();
49 }
50
51 bool Display::initialize()
52 {
53     if (isInitialized())
54     {
55         return true;
56     }
57
58     mD3d9Module = LoadLibrary(TEXT("d3d9.dll"));
59     if (mD3d9Module == NULL)
60     {
61         terminate();
62         return false;
63     }
64
65     typedef IDirect3D9* (WINAPI *Direct3DCreate9Func)(UINT);
66     Direct3DCreate9Func Direct3DCreate9Ptr = reinterpret_cast<Direct3DCreate9Func>(GetProcAddress(mD3d9Module, "Direct3DCreate9"));
67
68     if (Direct3DCreate9Ptr == NULL)
69     {
70         terminate();
71         return false;
72     }
73
74     typedef HRESULT (WINAPI *Direct3DCreate9ExFunc)(UINT, IDirect3D9Ex**);
75     Direct3DCreate9ExFunc Direct3DCreate9ExPtr = reinterpret_cast<Direct3DCreate9ExFunc>(GetProcAddress(mD3d9Module, "Direct3DCreate9Ex"));
76
77     // Use Direct3D9Ex if available. Among other things, this version is less
78     // inclined to report a lost context, for example when the user switches
79     // desktop. Direct3D9Ex is available in Windows Vista and later if suitable drivers are available.
80     if (Direct3DCreate9ExPtr && SUCCEEDED(Direct3DCreate9ExPtr(D3D_SDK_VERSION, &mD3d9ex)))
81     {
82         ASSERT(mD3d9ex);
83         mD3d9ex->QueryInterface(IID_IDirect3D9, reinterpret_cast<void**>(&mD3d9));
84         ASSERT(mD3d9);
85     }
86     else
87     {
88         mD3d9 = Direct3DCreate9Ptr(D3D_SDK_VERSION);
89     }
90
91     if (mD3d9)
92     {
93         if (mDc != NULL)
94         {
95         //  UNIMPLEMENTED();   // FIXME: Determine which adapter index the device context corresponds to
96         }
97
98         HRESULT result = mD3d9->GetDeviceCaps(mAdapter, mDeviceType, &mDeviceCaps);
99
100         if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
101         {
102             return error(EGL_BAD_ALLOC, false);
103         }
104
105         if (mDeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0))
106         {
107             terminate();
108             return error(EGL_NOT_INITIALIZED, false);
109         }
110
111         mMinSwapInterval = 4;
112         mMaxSwapInterval = 0;
113
114         if (mDeviceCaps.PresentationIntervals & D3DPRESENT_INTERVAL_IMMEDIATE) {mMinSwapInterval = std::min(mMinSwapInterval, 0); mMaxSwapInterval = std::max(mMaxSwapInterval, 0);}
115         if (mDeviceCaps.PresentationIntervals & D3DPRESENT_INTERVAL_ONE)       {mMinSwapInterval = std::min(mMinSwapInterval, 1); mMaxSwapInterval = std::max(mMaxSwapInterval, 1);}
116         if (mDeviceCaps.PresentationIntervals & D3DPRESENT_INTERVAL_TWO)       {mMinSwapInterval = std::min(mMinSwapInterval, 2); mMaxSwapInterval = std::max(mMaxSwapInterval, 2);}
117         if (mDeviceCaps.PresentationIntervals & D3DPRESENT_INTERVAL_THREE)     {mMinSwapInterval = std::min(mMinSwapInterval, 3); mMaxSwapInterval = std::max(mMaxSwapInterval, 3);}
118         if (mDeviceCaps.PresentationIntervals & D3DPRESENT_INTERVAL_FOUR)      {mMinSwapInterval = std::min(mMinSwapInterval, 4); mMaxSwapInterval = std::max(mMaxSwapInterval, 4);}
119
120         const D3DFORMAT renderTargetFormats[] =
121         {
122             D3DFMT_A1R5G5B5,
123         //  D3DFMT_A2R10G10B10,   // The color_ramp conformance test uses ReadPixels with UNSIGNED_BYTE causing it to think that rendering skipped a colour value.
124             D3DFMT_A8R8G8B8,
125             D3DFMT_R5G6B5,
126             D3DFMT_X1R5G5B5,
127             D3DFMT_X8R8G8B8
128         };
129
130         const D3DFORMAT depthStencilFormats[] =
131         {
132         //  D3DFMT_D16_LOCKABLE,
133             D3DFMT_D32,
134         //  D3DFMT_D15S1,
135             D3DFMT_D24S8,
136             D3DFMT_D24X8,
137         //  D3DFMT_D24X4S4,
138             D3DFMT_D16,
139         //  D3DFMT_D32F_LOCKABLE,
140         //  D3DFMT_D24FS8
141         };
142
143         D3DDISPLAYMODE currentDisplayMode;
144         mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
145
146         ConfigSet configSet;
147
148         for (int formatIndex = 0; formatIndex < sizeof(renderTargetFormats) / sizeof(D3DFORMAT); formatIndex++)
149         {
150             D3DFORMAT renderTargetFormat = renderTargetFormats[formatIndex];
151
152             HRESULT result = mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, renderTargetFormat);
153
154             if (SUCCEEDED(result))
155             {
156                 for (int depthStencilIndex = 0; depthStencilIndex < sizeof(depthStencilFormats) / sizeof(D3DFORMAT); depthStencilIndex++)
157                 {
158                     D3DFORMAT depthStencilFormat = depthStencilFormats[depthStencilIndex];
159                     HRESULT result = mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, depthStencilFormat);
160
161                     if (SUCCEEDED(result))
162                     {
163                         HRESULT result = mD3d9->CheckDepthStencilMatch(mAdapter, mDeviceType, currentDisplayMode.Format, renderTargetFormat, depthStencilFormat);
164
165                         if (SUCCEEDED(result))
166                         {
167                             // FIXME: enumerate multi-sampling
168
169                             configSet.add(currentDisplayMode, mMinSwapInterval, mMaxSwapInterval, renderTargetFormat, depthStencilFormat, 0);
170                         }
171                     }
172                 }
173             }
174         }
175
176         // Give the sorted configs a unique ID and store them internally
177         EGLint index = 1;
178         for (ConfigSet::Iterator config = configSet.mSet.begin(); config != configSet.mSet.end(); config++)
179         {
180             Config configuration = *config;
181             configuration.mConfigID = index;
182             index++;
183
184             mConfigSet.mSet.insert(configuration);
185         }
186
187         if (!createDevice())
188         {
189             terminate();
190
191             return false;
192         }
193     }
194
195     if (!isInitialized())
196     {
197         terminate();
198
199         return false;
200     }
201
202     return true;
203 }
204
205 void Display::terminate()
206 {
207     for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
208     {
209         delete *surface;
210     }
211
212     for (ContextSet::iterator context = mContextSet.begin(); context != mContextSet.end(); context++)
213     {
214         glDestroyContext(*context);
215     }
216
217     if (mDevice)
218     {
219         mDevice->Release();
220         mDevice = NULL;
221     }
222
223     if (mD3d9)
224     {
225         mD3d9->Release();
226         mD3d9 = NULL;
227     }
228
229     if (mDeviceWindow)
230     {
231         DestroyWindow(mDeviceWindow);
232         mDeviceWindow = NULL;
233     }
234     
235     if (mD3d9ex)
236     {
237         mD3d9ex->Release();
238         mD3d9ex = NULL;
239     }
240
241     if (mD3d9Module)
242     {
243         FreeLibrary(mD3d9Module);
244         mD3d9Module = NULL;
245     }
246 }
247
248 void Display::startScene()
249 {
250     if (!mSceneStarted)
251     {
252         long result = mDevice->BeginScene();
253         ASSERT(SUCCEEDED(result));
254         mSceneStarted = true;
255     }
256 }
257
258 void Display::endScene()
259 {
260     if (mSceneStarted)
261     {
262         long result = mDevice->EndScene();
263         ASSERT(SUCCEEDED(result));
264         mSceneStarted = false;
265     }
266 }
267
268 bool Display::getConfigs(EGLConfig *configs, const EGLint *attribList, EGLint configSize, EGLint *numConfig)
269 {
270     return mConfigSet.getConfigs(configs, attribList, configSize, numConfig);
271 }
272
273 bool Display::getConfigAttrib(EGLConfig config, EGLint attribute, EGLint *value)
274 {
275     const egl::Config *configuration = mConfigSet.get(config);
276
277     switch (attribute)
278     {
279       case EGL_BUFFER_SIZE:               *value = configuration->mBufferSize;             break;
280       case EGL_ALPHA_SIZE:                *value = configuration->mAlphaSize;              break;
281       case EGL_BLUE_SIZE:                 *value = configuration->mBlueSize;               break;
282       case EGL_GREEN_SIZE:                *value = configuration->mGreenSize;              break;
283       case EGL_RED_SIZE:                  *value = configuration->mRedSize;                break;
284       case EGL_DEPTH_SIZE:                *value = configuration->mDepthSize;              break;
285       case EGL_STENCIL_SIZE:              *value = configuration->mStencilSize;            break;
286       case EGL_CONFIG_CAVEAT:             *value = configuration->mConfigCaveat;           break;
287       case EGL_CONFIG_ID:                 *value = configuration->mConfigID;               break;
288       case EGL_LEVEL:                     *value = configuration->mLevel;                  break;
289       case EGL_NATIVE_RENDERABLE:         *value = configuration->mNativeRenderable;       break;
290       case EGL_NATIVE_VISUAL_TYPE:        *value = configuration->mNativeVisualType;       break;
291       case EGL_SAMPLES:                   *value = configuration->mSamples;                break;
292       case EGL_SAMPLE_BUFFERS:            *value = configuration->mSampleBuffers;          break;
293       case EGL_SURFACE_TYPE:              *value = configuration->mSurfaceType;            break;
294       case EGL_TRANSPARENT_TYPE:          *value = configuration->mTransparentType;        break;
295       case EGL_TRANSPARENT_BLUE_VALUE:    *value = configuration->mTransparentBlueValue;   break;
296       case EGL_TRANSPARENT_GREEN_VALUE:   *value = configuration->mTransparentGreenValue;  break;
297       case EGL_TRANSPARENT_RED_VALUE:     *value = configuration->mTransparentRedValue;    break;
298       case EGL_BIND_TO_TEXTURE_RGB:       *value = configuration->mBindToTextureRGB;       break;
299       case EGL_BIND_TO_TEXTURE_RGBA:      *value = configuration->mBindToTextureRGBA;      break;
300       case EGL_MIN_SWAP_INTERVAL:         *value = configuration->mMinSwapInterval;        break;
301       case EGL_MAX_SWAP_INTERVAL:         *value = configuration->mMaxSwapInterval;        break;
302       case EGL_LUMINANCE_SIZE:            *value = configuration->mLuminanceSize;          break;
303       case EGL_ALPHA_MASK_SIZE:           *value = configuration->mAlphaMaskSize;          break;
304       case EGL_COLOR_BUFFER_TYPE:         *value = configuration->mColorBufferType;        break;
305       case EGL_RENDERABLE_TYPE:           *value = configuration->mRenderableType;         break;
306       case EGL_MATCH_NATIVE_PIXMAP:       *value = false; UNIMPLEMENTED();                 break;
307       case EGL_CONFORMANT:                *value = configuration->mConformant;             break;
308       default:
309         return false;
310     }
311
312     return true;
313 }
314
315 bool Display::createDevice()
316 {
317     static const TCHAR windowName[] = TEXT("AngleHiddenWindow");
318     static const TCHAR className[] = TEXT("STATIC");
319
320     mDeviceWindow = CreateWindowEx(WS_EX_NOACTIVATE, className, windowName, WS_DISABLED | WS_POPUP, 0, 0, 1, 1, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
321
322     D3DPRESENT_PARAMETERS presentParameters = {0};
323
324     // The default swap chain is never actually used. Surface will create a new swap chain with the proper parameters.
325     presentParameters.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
326     presentParameters.BackBufferCount = 1;
327     presentParameters.BackBufferFormat = D3DFMT_UNKNOWN;
328     presentParameters.BackBufferWidth = 1;
329     presentParameters.BackBufferHeight = 1;
330     presentParameters.EnableAutoDepthStencil = FALSE;
331     presentParameters.Flags = 0;
332     presentParameters.hDeviceWindow = mDeviceWindow;
333     presentParameters.MultiSampleQuality = 0;
334     presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;
335     presentParameters.PresentationInterval = convertInterval(mMinSwapInterval);
336     presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
337     presentParameters.Windowed = TRUE;
338
339     DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_NOWINDOWCHANGES;
340
341     HRESULT result = mD3d9->CreateDevice(mAdapter, mDeviceType, mDeviceWindow, behaviorFlags | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE, &presentParameters, &mDevice);
342
343     if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
344     {
345         return error(EGL_BAD_ALLOC, false);
346     }
347
348     if (FAILED(result))
349     {
350         result = mD3d9->CreateDevice(mAdapter, mDeviceType, mDeviceWindow, behaviorFlags | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentParameters, &mDevice);
351
352         if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
353         {
354             return error(EGL_BAD_ALLOC, false);
355         }
356     }
357
358     ASSERT(SUCCEEDED(result));
359
360     // Permanent non-default states
361     mDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
362
363     mSceneStarted = false;
364
365     return true;
366 }
367
368 Surface *Display::createWindowSurface(HWND window, EGLConfig config)
369 {
370     const Config *configuration = mConfigSet.get(config);
371
372     Surface *surface = new Surface(this, configuration, window);
373     mSurfaceSet.insert(surface);
374
375     return surface;
376 }
377
378 EGLContext Display::createContext(EGLConfig configHandle, const gl::Context *shareContext)
379 {
380     const egl::Config *config = mConfigSet.get(configHandle);
381
382     gl::Context *context = glCreateContext(config, shareContext);
383     mContextSet.insert(context);
384
385     return context;
386 }
387
388 void Display::destroySurface(egl::Surface *surface)
389 {
390     delete surface;
391     mSurfaceSet.erase(surface);
392 }
393
394 void Display::destroyContext(gl::Context *context)
395 {
396     glDestroyContext(context);
397     mContextSet.erase(context);
398 }
399
400 bool Display::isInitialized()
401 {
402     return mD3d9 != NULL && mConfigSet.size() > 0;
403 }
404
405 bool Display::isValidConfig(EGLConfig config)
406 {
407     return mConfigSet.get(config) != NULL;
408 }
409
410 bool Display::isValidContext(gl::Context *context)
411 {
412     return mContextSet.find(context) != mContextSet.end();
413 }
414
415 bool Display::isValidSurface(egl::Surface *surface)
416 {
417     return mSurfaceSet.find(surface) != mSurfaceSet.end();
418 }
419
420 bool Display::hasExistingWindowSurface(HWND window)
421 {
422     for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
423     {
424         if ((*surface)->getWindowHandle() == window)
425         {
426             return true;
427         }
428     }
429
430     return false;
431 }
432
433 void Display::setSwapInterval(GLint interval)
434 {
435     mSwapInterval = interval;
436     mSwapInterval = std::max(mSwapInterval, mMinSwapInterval);
437     mSwapInterval = std::min(mSwapInterval, mMaxSwapInterval);
438
439     mPresentInterval = convertInterval(mSwapInterval);
440 }
441
442 DWORD Display::getPresentInterval()
443 {
444     return mPresentInterval;
445 }
446
447 DWORD Display::convertInterval(GLint interval)
448 {
449     switch(interval)
450     {
451       case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
452       case 1: return D3DPRESENT_INTERVAL_ONE;
453       case 2: return D3DPRESENT_INTERVAL_TWO;
454       case 3: return D3DPRESENT_INTERVAL_THREE;
455       case 4: return D3DPRESENT_INTERVAL_FOUR;
456       default: UNREACHABLE();
457     }
458
459     return D3DPRESENT_INTERVAL_DEFAULT;
460 }
461
462 IDirect3DDevice9 *Display::getDevice()
463 {
464     return mDevice;
465 }
466
467 D3DCAPS9 Display::getDeviceCaps()
468 {
469     return mDeviceCaps;
470 }
471
472 void Display::getMultiSampleSupport(D3DFORMAT format, bool *multiSampleArray)
473 {
474     for (int multiSampleIndex = 0; multiSampleIndex <= D3DMULTISAMPLE_16_SAMPLES; multiSampleIndex++)
475     {
476         HRESULT result = mD3d9->CheckDeviceMultiSampleType(mAdapter, mDeviceType, format,
477                                                            TRUE, (D3DMULTISAMPLE_TYPE)multiSampleIndex, NULL);
478
479         multiSampleArray[multiSampleIndex] = SUCCEEDED(result);
480     }
481 }
482
483 bool Display::getCompressedTextureSupport()
484 {
485     D3DDISPLAYMODE currentDisplayMode;
486     mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
487
488     return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT1));
489 }
490 }