1 /*
2  *  Copyright (C) 2017-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #pragma once
10 
11 #include <array>
12 #include <set>
13 #include <stdexcept>
14 #include <string>
15 #include <vector>
16 
17 #include "system_egl.h"
18 
19 class CEGLUtils
20 {
21 public:
22   static std::set<std::string> GetClientExtensions();
23   static std::set<std::string> GetExtensions(EGLDisplay eglDisplay);
24   static bool HasExtension(EGLDisplay eglDisplay, std::string const & name);
25   static bool HasClientExtension(std::string const& name);
26   static void Log(int logLevel, std::string const& what);
27   template<typename T>
GetRequiredProcAddress(const char * procname)28   static T GetRequiredProcAddress(const char * procname)
29   {
30     T p = reinterpret_cast<T>(eglGetProcAddress(procname));
31     if (!p)
32     {
33       throw std::runtime_error(std::string("Could not get EGL function \"") + procname + "\" - maybe a required extension is not supported?");
34     }
35     return p;
36   }
37 
38 private:
39   CEGLUtils();
40 };
41 
42 /**
43  * Convenience wrapper for heap-allocated EGL attribute arrays
44  *
45  * The wrapper makes sure that the key/value pairs are always written in actual
46  * pairs and  that the array is always terminated with EGL_NONE.
47  */
48 class CEGLAttributesVec
49 {
50 public:
51   struct EGLAttribute
52   {
53     EGLint key;
54     EGLint value;
55   };
56 
57   /**
58    * Add multiple attributes
59    *
60    * The array is automatically terminated with EGL_NONE
61    */
Add(std::initializer_list<EGLAttribute> const & attributes)62   void Add(std::initializer_list<EGLAttribute> const& attributes)
63   {
64     for (auto const& attribute : attributes)
65     {
66       m_attributes.insert(m_attributes.begin(), attribute.value);
67       m_attributes.insert(m_attributes.begin(), attribute.key);
68     }
69   }
70 
71   /**
72    * Add one attribute
73    *
74    * The array is automatically terminated with EGL_NONE
75    */
Add(EGLAttribute const & attribute)76   void Add(EGLAttribute const& attribute)
77   {
78     Add({attribute});
79   }
80 
Get()81   EGLint const * Get() const
82   {
83     return m_attributes.data();
84   }
85 
86 private:
87   std::vector<EGLint> m_attributes{EGL_NONE};
88 };
89 
90 /**
91  * Convenience wrapper for stack-allocated EGL attribute arrays
92  *
93  * The wrapper makes sure that the key/value pairs are always written in actual
94  * pairs, that the array is always terminated with EGL_NONE, and that the bounds
95  * of the array are not exceeded (checked on runtime).
96  *
97  * \tparam AttributeCount maximum number of attributes that can be added.
98  *                        Determines the size of the storage array.
99  */
100 template<std::size_t AttributeCount>
101 class CEGLAttributes
102 {
103 public:
104   struct EGLAttribute
105   {
106     EGLint key;
107     EGLint value;
108   };
109 
CEGLAttributes()110   CEGLAttributes()
111   {
112     m_attributes[0] = EGL_NONE;
113   }
114 
115   /**
116    * Add multiple attributes
117    *
118    * The array is automatically terminated with EGL_NONE
119    *
120    * \throws std::out_of_range if more than AttributeCount attributes are added
121    *                           in total
122    */
Add(std::initializer_list<EGLAttribute> const & attributes)123   void Add(std::initializer_list<EGLAttribute> const& attributes)
124   {
125     if (m_writePosition + attributes.size() * 2 + 1 > m_attributes.size())
126     {
127       throw std::out_of_range("CEGLAttributes::Add");
128     }
129 
130     for (auto const& attribute : attributes)
131     {
132       m_attributes[m_writePosition++] = attribute.key;
133       m_attributes[m_writePosition++] = attribute.value;
134     }
135     m_attributes[m_writePosition] = EGL_NONE;
136   }
137 
138   /**
139    * Add one attribute
140    *
141    * The array is automatically terminated with EGL_NONE
142    *
143    * \throws std::out_of_range if more than AttributeCount attributes are added
144    *                           in total
145    */
Add(EGLAttribute const & attribute)146   void Add(EGLAttribute const& attribute)
147   {
148     Add({attribute});
149   }
150 
Get()151   EGLint const * Get() const
152   {
153     return m_attributes.data();
154   }
155 
Size()156   int Size() const
157   {
158     return m_writePosition;
159   }
160 
161 private:
162   std::array<EGLint, AttributeCount * 2 + 1> m_attributes;
163   int m_writePosition{};
164 };
165 
166 class CEGLContextUtils final
167 {
168 public:
169   CEGLContextUtils() = default;
170   /**
171    * \param platform platform as constant from an extension building on EGL_EXT_platform_base
172    */
173   CEGLContextUtils(EGLenum platform, std::string const& platformExtension);
174   ~CEGLContextUtils();
175 
176   bool CreateDisplay(EGLNativeDisplayType nativeDisplay);
177   /**
178    * Create EGLDisplay with EGL_EXT_platform_base
179    *
180    * Falls back to \ref CreateDisplay (with nativeDisplayLegacy) on failure.
181    * The native displays to use with the platform-based and the legacy approach
182    * may be defined to have different types and/or semantics, so this function takes
183    * both as separate parameters.
184    *
185    * \param nativeDisplay native display to use with eglGetPlatformDisplayEXT
186    * \param nativeDisplayLegacy native display to use with eglGetDisplay
187    */
188   bool CreatePlatformDisplay(void* nativeDisplay, EGLNativeDisplayType nativeDisplayLegacy);
189 
190   void SurfaceAttrib(EGLint attribute, EGLint value);
191   bool CreateSurface(EGLNativeWindowType nativeWindow, EGLint HDRcolorSpace = EGL_NONE);
192   bool CreatePlatformSurface(void* nativeWindow, EGLNativeWindowType nativeWindowLegacy);
193   bool InitializeDisplay(EGLint renderingApi);
194   bool ChooseConfig(EGLint renderableType, EGLint visualId = 0, bool hdr = false);
195   bool CreateContext(CEGLAttributesVec contextAttribs);
196   bool BindContext();
197   void Destroy();
198   void DestroySurface();
199   void DestroyContext();
200   bool SetVSync(bool enable);
201   bool TrySwapBuffers();
202   bool IsPlatformSupported() const;
203   EGLint GetConfigAttrib(EGLint attribute) const;
204 
GetEGLDisplay()205   EGLDisplay GetEGLDisplay() const
206   {
207     return m_eglDisplay;
208   }
GetEGLSurface()209   EGLSurface GetEGLSurface() const
210   {
211     return m_eglSurface;
212   }
GetEGLContext()213   EGLContext GetEGLContext() const
214   {
215     return m_eglContext;
216   }
GetEGLConfig()217   EGLConfig GetEGLConfig() const
218   {
219     return m_eglConfig;
220   }
221 
222 private:
223   void SurfaceAttrib();
224 
225   EGLenum m_platform{EGL_NONE};
226   bool m_platformSupported{false};
227 
228   EGLDisplay m_eglDisplay{EGL_NO_DISPLAY};
229   EGLSurface m_eglSurface{EGL_NO_SURFACE};
230   EGLContext m_eglContext{EGL_NO_CONTEXT};
231   EGLConfig m_eglConfig{}, m_eglHDRConfig{};
232 };
233