1//
2// Copyright (c) 2002-2014 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 <map>
15#include <vector>
16#include <sstream>
17
18#include "common/debug.h"
19#include "common/mathutil.h"
20#include "libGLESv2/main.h"
21#include "libGLESv2/Context.h"
22#include "libGLESv2/renderer/SwapChain.h"
23
24#include "libEGL/main.h"
25#include "libEGL/Surface.h"
26
27namespace egl
28{
29
30typedef std::map<EGLNativeDisplayType, Display*> DisplayMap;
31static DisplayMap *GetDisplayMap()
32{
33    static DisplayMap displays;
34    return &displays;
35}
36
37egl::Display *Display::getDisplay(EGLNativeDisplayType displayId, EGLint displayType)
38{
39    DisplayMap *displays = GetDisplayMap();
40    DisplayMap::const_iterator iter = displays->find(displayId);
41    if (iter != displays->end())
42    {
43        return iter->second;
44    }
45
46    // FIXME: Check if displayId is a valid display device context
47
48    egl::Display *display = new egl::Display(displayId, displayType);
49    displays->insert(std::make_pair(displayId, display));
50
51    return display;
52}
53
54Display::Display(EGLNativeDisplayType displayId, EGLint displayType)
55    : mDisplayId(displayId),
56      mRequestedDisplayType(displayType),
57      mRenderer(NULL)
58{
59}
60
61Display::~Display()
62{
63    terminate();
64
65    DisplayMap *displays = GetDisplayMap();
66    DisplayMap::iterator iter = displays->find(mDisplayId);
67    if (iter != displays->end())
68    {
69        displays->erase(iter);
70    }
71}
72
73bool Display::initialize()
74{
75    if (isInitialized())
76    {
77        return true;
78    }
79
80    mRenderer = glCreateRenderer(this, mDisplayId, mRequestedDisplayType);
81
82    if (!mRenderer)
83    {
84        terminate();
85        return error(EGL_NOT_INITIALIZED, false);
86    }
87
88    EGLint minSwapInterval = mRenderer->getMinSwapInterval();
89    EGLint maxSwapInterval = mRenderer->getMaxSwapInterval();
90    EGLint maxTextureSize = mRenderer->getRendererCaps().max2DTextureSize;
91
92    rx::ConfigDesc *descList;
93    int numConfigs = mRenderer->generateConfigs(&descList);
94    ConfigSet configSet;
95
96    for (int i = 0; i < numConfigs; ++i)
97    {
98        configSet.add(descList[i], minSwapInterval, maxSwapInterval, maxTextureSize, maxTextureSize);
99    }
100
101    // Give the sorted configs a unique ID and store them internally
102    EGLint index = 1;
103    for (ConfigSet::Iterator config = configSet.mSet.begin(); config != configSet.mSet.end(); config++)
104    {
105        Config configuration = *config;
106        configuration.mConfigID = index;
107        index++;
108
109        mConfigSet.mSet.insert(configuration);
110    }
111
112    mRenderer->deleteConfigs(descList);
113    descList = NULL;
114
115    if (!isInitialized())
116    {
117        terminate();
118        return false;
119    }
120
121    initDisplayExtensionString();
122    initVendorString();
123
124    return true;
125}
126
127void Display::terminate()
128{
129    while (!mSurfaceSet.empty())
130    {
131        destroySurface(*mSurfaceSet.begin());
132    }
133
134    while (!mContextSet.empty())
135    {
136        destroyContext(*mContextSet.begin());
137    }
138
139    glDestroyRenderer(mRenderer);
140    mRenderer = NULL;
141}
142
143bool Display::getConfigs(EGLConfig *configs, const EGLint *attribList, EGLint configSize, EGLint *numConfig)
144{
145    return mConfigSet.getConfigs(configs, attribList, configSize, numConfig);
146}
147
148bool Display::getConfigAttrib(EGLConfig config, EGLint attribute, EGLint *value)
149{
150    const egl::Config *configuration = mConfigSet.get(config);
151
152    switch (attribute)
153    {
154      case EGL_BUFFER_SIZE:               *value = configuration->mBufferSize;             break;
155      case EGL_ALPHA_SIZE:                *value = configuration->mAlphaSize;              break;
156      case EGL_BLUE_SIZE:                 *value = configuration->mBlueSize;               break;
157      case EGL_GREEN_SIZE:                *value = configuration->mGreenSize;              break;
158      case EGL_RED_SIZE:                  *value = configuration->mRedSize;                break;
159      case EGL_DEPTH_SIZE:                *value = configuration->mDepthSize;              break;
160      case EGL_STENCIL_SIZE:              *value = configuration->mStencilSize;            break;
161      case EGL_CONFIG_CAVEAT:             *value = configuration->mConfigCaveat;           break;
162      case EGL_CONFIG_ID:                 *value = configuration->mConfigID;               break;
163      case EGL_LEVEL:                     *value = configuration->mLevel;                  break;
164      case EGL_NATIVE_RENDERABLE:         *value = configuration->mNativeRenderable;       break;
165      case EGL_NATIVE_VISUAL_TYPE:        *value = configuration->mNativeVisualType;       break;
166      case EGL_SAMPLES:                   *value = configuration->mSamples;                break;
167      case EGL_SAMPLE_BUFFERS:            *value = configuration->mSampleBuffers;          break;
168      case EGL_SURFACE_TYPE:              *value = configuration->mSurfaceType;            break;
169      case EGL_TRANSPARENT_TYPE:          *value = configuration->mTransparentType;        break;
170      case EGL_TRANSPARENT_BLUE_VALUE:    *value = configuration->mTransparentBlueValue;   break;
171      case EGL_TRANSPARENT_GREEN_VALUE:   *value = configuration->mTransparentGreenValue;  break;
172      case EGL_TRANSPARENT_RED_VALUE:     *value = configuration->mTransparentRedValue;    break;
173      case EGL_BIND_TO_TEXTURE_RGB:       *value = configuration->mBindToTextureRGB;       break;
174      case EGL_BIND_TO_TEXTURE_RGBA:      *value = configuration->mBindToTextureRGBA;      break;
175      case EGL_MIN_SWAP_INTERVAL:         *value = configuration->mMinSwapInterval;        break;
176      case EGL_MAX_SWAP_INTERVAL:         *value = configuration->mMaxSwapInterval;        break;
177      case EGL_LUMINANCE_SIZE:            *value = configuration->mLuminanceSize;          break;
178      case EGL_ALPHA_MASK_SIZE:           *value = configuration->mAlphaMaskSize;          break;
179      case EGL_COLOR_BUFFER_TYPE:         *value = configuration->mColorBufferType;        break;
180      case EGL_RENDERABLE_TYPE:           *value = configuration->mRenderableType;         break;
181      case EGL_MATCH_NATIVE_PIXMAP:       *value = false; UNIMPLEMENTED();                 break;
182      case EGL_CONFORMANT:                *value = configuration->mConformant;             break;
183      case EGL_MAX_PBUFFER_WIDTH:         *value = configuration->mMaxPBufferWidth;        break;
184      case EGL_MAX_PBUFFER_HEIGHT:        *value = configuration->mMaxPBufferHeight;       break;
185      case EGL_MAX_PBUFFER_PIXELS:        *value = configuration->mMaxPBufferPixels;       break;
186      default:
187        return false;
188    }
189
190    return true;
191}
192
193
194
195EGLSurface Display::createWindowSurface(HWND window, EGLConfig config, const EGLint *attribList)
196{
197    const Config *configuration = mConfigSet.get(config);
198    EGLint postSubBufferSupported = EGL_FALSE;
199
200    EGLint width = 0;
201    EGLint height = 0;
202    EGLint fixedSize = EGL_FALSE;
203
204    if (attribList)
205    {
206        while (*attribList != EGL_NONE)
207        {
208            switch (attribList[0])
209            {
210              case EGL_RENDER_BUFFER:
211                switch (attribList[1])
212                {
213                  case EGL_BACK_BUFFER:
214                    break;
215                  case EGL_SINGLE_BUFFER:
216                    return error(EGL_BAD_MATCH, EGL_NO_SURFACE);   // Rendering directly to front buffer not supported
217                  default:
218                    return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
219                }
220                break;
221              case EGL_POST_SUB_BUFFER_SUPPORTED_NV:
222                postSubBufferSupported = attribList[1];
223                break;
224              case EGL_WIDTH:
225                width = attribList[1];
226                break;
227              case EGL_HEIGHT:
228                height = attribList[1];
229                break;
230              case EGL_FIXED_SIZE_ANGLE:
231                fixedSize = attribList[1];
232                break;
233              case EGL_VG_COLORSPACE:
234                return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
235              case EGL_VG_ALPHA_FORMAT:
236                return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
237              default:
238                return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
239            }
240
241            attribList += 2;
242        }
243    }
244
245    if (width < 0 || height < 0)
246    {
247        return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
248    }
249
250    if (!fixedSize)
251    {
252        width = -1;
253        height = -1;
254    }
255
256    if (hasExistingWindowSurface(window))
257    {
258        return error(EGL_BAD_ALLOC, EGL_NO_SURFACE);
259    }
260
261    if (mRenderer->testDeviceLost(false))
262    {
263        if (!restoreLostDevice())
264            return EGL_NO_SURFACE;
265    }
266
267    Surface *surface = new Surface(this, configuration, window, fixedSize, width, height, postSubBufferSupported);
268
269    if (!surface->initialize())
270    {
271        delete surface;
272        return EGL_NO_SURFACE;
273    }
274
275    mSurfaceSet.insert(surface);
276
277    return success(surface);
278}
279
280EGLSurface Display::createOffscreenSurface(EGLConfig config, HANDLE shareHandle, const EGLint *attribList)
281{
282    EGLint width = 0, height = 0;
283    EGLenum textureFormat = EGL_NO_TEXTURE;
284    EGLenum textureTarget = EGL_NO_TEXTURE;
285    const Config *configuration = mConfigSet.get(config);
286
287    if (attribList)
288    {
289        while (*attribList != EGL_NONE)
290        {
291            switch (attribList[0])
292            {
293              case EGL_WIDTH:
294                width = attribList[1];
295                break;
296              case EGL_HEIGHT:
297                height = attribList[1];
298                break;
299              case EGL_LARGEST_PBUFFER:
300                if (attribList[1] != EGL_FALSE)
301                  UNIMPLEMENTED(); // FIXME
302                break;
303              case EGL_TEXTURE_FORMAT:
304                switch (attribList[1])
305                {
306                  case EGL_NO_TEXTURE:
307                  case EGL_TEXTURE_RGB:
308                  case EGL_TEXTURE_RGBA:
309                    textureFormat = attribList[1];
310                    break;
311                  default:
312                    return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
313                }
314                break;
315              case EGL_TEXTURE_TARGET:
316                switch (attribList[1])
317                {
318                  case EGL_NO_TEXTURE:
319                  case EGL_TEXTURE_2D:
320                    textureTarget = attribList[1];
321                    break;
322                  default:
323                    return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
324                }
325                break;
326              case EGL_MIPMAP_TEXTURE:
327                if (attribList[1] != EGL_FALSE)
328                  return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
329                break;
330              case EGL_VG_COLORSPACE:
331                return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
332              case EGL_VG_ALPHA_FORMAT:
333                return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
334              default:
335                return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
336            }
337
338            attribList += 2;
339        }
340    }
341
342    if (width < 0 || height < 0)
343    {
344        return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
345    }
346
347    if (width == 0 || height == 0)
348    {
349        return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
350    }
351
352    if (textureFormat != EGL_NO_TEXTURE && !mRenderer->getRendererExtensions().textureNPOT && (!gl::isPow2(width) || !gl::isPow2(height)))
353    {
354        return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
355    }
356
357    if ((textureFormat != EGL_NO_TEXTURE && textureTarget == EGL_NO_TEXTURE) ||
358        (textureFormat == EGL_NO_TEXTURE && textureTarget != EGL_NO_TEXTURE))
359    {
360        return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
361    }
362
363    if (!(configuration->mSurfaceType & EGL_PBUFFER_BIT))
364    {
365        return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
366    }
367
368    if ((textureFormat == EGL_TEXTURE_RGB && configuration->mBindToTextureRGB != EGL_TRUE) ||
369        (textureFormat == EGL_TEXTURE_RGBA && configuration->mBindToTextureRGBA != EGL_TRUE))
370    {
371        return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
372    }
373
374    if (mRenderer->testDeviceLost(false))
375    {
376        if (!restoreLostDevice())
377            return EGL_NO_SURFACE;
378    }
379
380    Surface *surface = new Surface(this, configuration, shareHandle, width, height, textureFormat, textureTarget);
381
382    if (!surface->initialize())
383    {
384        delete surface;
385        return EGL_NO_SURFACE;
386    }
387
388    mSurfaceSet.insert(surface);
389
390    return success(surface);
391}
392
393EGLContext Display::createContext(EGLConfig configHandle, EGLint clientVersion, const gl::Context *shareContext, bool notifyResets, bool robustAccess)
394{
395    if (!mRenderer)
396    {
397        return EGL_NO_CONTEXT;
398    }
399    else if (mRenderer->testDeviceLost(false))   // Lost device
400    {
401        if (!restoreLostDevice())
402        {
403            return error(EGL_CONTEXT_LOST, EGL_NO_CONTEXT);
404        }
405    }
406
407    if (clientVersion > 2 && mRenderer->getMajorShaderModel() < 4)
408    {
409        return error(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
410    }
411
412    gl::Context *context = glCreateContext(clientVersion, shareContext, mRenderer, notifyResets, robustAccess);
413    mContextSet.insert(context);
414
415    return success(context);
416}
417
418bool Display::restoreLostDevice()
419{
420    for (ContextSet::iterator ctx = mContextSet.begin(); ctx != mContextSet.end(); ctx++)
421    {
422        if ((*ctx)->isResetNotificationEnabled())
423            return false;   // If reset notifications have been requested, application must delete all contexts first
424    }
425
426    // Release surface resources to make the Reset() succeed
427    for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
428    {
429        (*surface)->release();
430    }
431
432    if (!mRenderer->resetDevice())
433    {
434        return error(EGL_BAD_ALLOC, false);
435    }
436
437    // Restore any surfaces that may have been lost
438    for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
439    {
440        (*surface)->resetSwapChain();
441    }
442
443    return true;
444}
445
446
447void Display::destroySurface(egl::Surface *surface)
448{
449    delete surface;
450    mSurfaceSet.erase(surface);
451}
452
453void Display::destroyContext(gl::Context *context)
454{
455    glDestroyContext(context);
456    mContextSet.erase(context);
457}
458
459void Display::notifyDeviceLost()
460{
461    for (ContextSet::iterator context = mContextSet.begin(); context != mContextSet.end(); context++)
462    {
463        (*context)->markContextLost();
464    }
465    egl::error(EGL_CONTEXT_LOST);
466}
467
468void Display::recreateSwapChains()
469{
470    for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
471    {
472        (*surface)->getSwapChain()->recreate();
473    }
474}
475
476bool Display::isInitialized() const
477{
478    return mRenderer != NULL && mConfigSet.size() > 0;
479}
480
481bool Display::isValidConfig(EGLConfig config)
482{
483    return mConfigSet.get(config) != NULL;
484}
485
486bool Display::isValidContext(gl::Context *context)
487{
488    return mContextSet.find(context) != mContextSet.end();
489}
490
491bool Display::isValidSurface(egl::Surface *surface)
492{
493    return mSurfaceSet.find(surface) != mSurfaceSet.end();
494}
495
496bool Display::hasExistingWindowSurface(HWND window)
497{
498    for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
499    {
500        if ((*surface)->getWindowHandle() == window)
501        {
502            return true;
503        }
504    }
505
506    return false;
507}
508
509std::string Display::generateClientExtensionString()
510{
511    std::vector<std::string> extensions;
512
513    extensions.push_back("EGL_EXT_client_extensions");
514
515    extensions.push_back("ANGLE_platform_angle");
516
517    if (supportsPlatformD3D())
518    {
519        extensions.push_back("ANGLE_platform_angle_d3d");
520    }
521
522    if (supportsPlatformOpenGL())
523    {
524        extensions.push_back("ANGLE_platform_angle_opengl");
525    }
526
527    std::ostringstream stream;
528    std::copy(extensions.begin(), extensions.end(), std::ostream_iterator<std::string>(stream, " "));
529    return stream.str();
530}
531
532void Display::initDisplayExtensionString()
533{
534    std::vector<std::string> extensions;
535
536    // Multi-vendor (EXT) extensions
537    extensions.push_back("EGL_EXT_create_context_robustness");
538
539    // ANGLE-specific extensions
540    if (mRenderer->getShareHandleSupport())
541    {
542        extensions.push_back("EGL_ANGLE_d3d_share_handle_client_buffer");
543        extensions.push_back("EGL_ANGLE_surface_d3d_texture_2d_share_handle");
544    }
545
546    extensions.push_back("EGL_ANGLE_query_surface_pointer");
547    extensions.push_back("EGL_ANGLE_window_fixed_size");
548
549    if (mRenderer->getPostSubBufferSupport())
550    {
551        extensions.push_back("EGL_NV_post_sub_buffer");
552    }
553
554    // TODO: complete support for the EGL_KHR_create_context extension
555    extensions.push_back("EGL_KHR_create_context");
556
557    std::ostringstream stream;
558    std::copy(extensions.begin(), extensions.end(), std::ostream_iterator<std::string>(stream, " "));
559    mDisplayExtensionString = stream.str();
560}
561
562const char *Display::getExtensionString(egl::Display *display)
563{
564    if (display != EGL_NO_DISPLAY)
565    {
566        return display->mDisplayExtensionString.c_str();
567    }
568    else
569    {
570        static std::string clientExtensions = generateClientExtensionString();
571        return clientExtensions.c_str();
572    }
573}
574
575bool Display::supportsPlatformD3D()
576{
577#if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11)
578    return true;
579#else
580    return false;
581#endif
582}
583
584bool Display::supportsPlatformOpenGL()
585{
586    return false;
587}
588
589void Display::initVendorString()
590{
591    mVendorString = "Google Inc.";
592
593    LUID adapterLuid = {0};
594
595    if (mRenderer && mRenderer->getLUID(&adapterLuid))
596    {
597        char adapterLuidString[64];
598        sprintf_s(adapterLuidString, sizeof(adapterLuidString), " (adapter LUID: %08x%08x)", adapterLuid.HighPart, adapterLuid.LowPart);
599
600        mVendorString += adapterLuidString;
601    }
602}
603
604const char *Display::getVendorString() const
605{
606    return mVendorString.c_str();
607}
608
609}
610