Panda3D
eglGraphicsStateGuardian.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file eglGraphicsStateGuardian.cxx
10  * @author rdb
11  * @date 2009-05-21
12  */
13 
15 #include "config_egldisplay.h"
16 #include "lightReMutexHolder.h"
17 
18 #include <dlfcn.h>
19 
20 TypeHandle eglGraphicsStateGuardian::_type_handle;
21 
22 /**
23  *
24  */
25 eglGraphicsStateGuardian::
26 eglGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
27  eglGraphicsStateGuardian *share_with) :
28  BaseGraphicsStateGuardian(engine, pipe)
29 {
30  _share_context=0;
31  _context=0;
32  _egl_display=0;
33  _fbconfig=0;
34 
35  if (share_with != nullptr) {
36  _prepared_objects = share_with->get_prepared_objects();
37  _share_context = share_with->_context;
38  }
39 }
40 
41 /**
42  *
43  */
44 eglGraphicsStateGuardian::
45 ~eglGraphicsStateGuardian() {
46  if (_context != (EGLContext)nullptr) {
47  if (!eglDestroyContext(_egl_display, _context)) {
48  egldisplay_cat.error() << "Failed to destroy EGL context: "
49  << get_egl_error_string(eglGetError()) << "\n";
50  }
51  _context = (EGLContext)nullptr;
52  }
53 }
54 
55 /**
56  * Gets the FrameBufferProperties to match the indicated config.
57  */
60  bool &pbuffer_supported, bool &pixmap_supported,
61  bool &slow, EGLConfig config) {
62 
63  properties.clear();
64 
65  // Now update our framebuffer_mode and bit depth appropriately.
66  EGLint red_size, green_size, blue_size,
67  alpha_size,
68  depth_size, stencil_size, samples, surface_type, caveat;
69 
70  eglGetConfigAttrib(_egl_display, config, EGL_RED_SIZE, &red_size);
71  eglGetConfigAttrib(_egl_display, config, EGL_GREEN_SIZE, &green_size);
72  eglGetConfigAttrib(_egl_display, config, EGL_BLUE_SIZE, &blue_size);
73  eglGetConfigAttrib(_egl_display, config, EGL_ALPHA_SIZE, &alpha_size);
74  eglGetConfigAttrib(_egl_display, config, EGL_DEPTH_SIZE, &depth_size);
75  eglGetConfigAttrib(_egl_display, config, EGL_STENCIL_SIZE, &stencil_size);
76  eglGetConfigAttrib(_egl_display, config, EGL_SAMPLES, &samples);
77  eglGetConfigAttrib(_egl_display, config, EGL_SURFACE_TYPE, &surface_type);
78  eglGetConfigAttrib(_egl_display, config, EGL_CONFIG_CAVEAT, &caveat);
79  int err = eglGetError();
80  if (err != EGL_SUCCESS) {
81  egldisplay_cat.error() << "Failed to get EGL config attrib: "
82  << get_egl_error_string(err) << "\n";
83  }
84 
85  pbuffer_supported = false;
86  if ((surface_type & EGL_PBUFFER_BIT)!=0) {
87  pbuffer_supported = true;
88  }
89 
90  pixmap_supported = false;
91  if ((surface_type & EGL_PIXMAP_BIT)!=0) {
92  pixmap_supported = true;
93  }
94 
95  slow = false;
96  if (caveat == EGL_SLOW_CONFIG) {
97  slow = true;
98  }
99 
100  properties.set_back_buffers(1);
101  properties.set_rgb_color(1);
102  properties.set_rgba_bits(red_size, green_size, blue_size, alpha_size);
103  properties.set_stencil_bits(stencil_size);
104  properties.set_depth_bits(depth_size);
105  properties.set_multisamples(samples);
106 
107  // "slow" likely indicates no hardware acceleration.
108  properties.set_force_software(slow);
109  properties.set_force_hardware(!slow);
110 }
111 
112 /**
113  * Selects a visual or fbconfig for all the windows and buffers that use this
114  * gsg. Also creates the GL context and obtains the visual.
115  */
118  eglGraphicsPipe *egl_pipe, bool need_window,
119  bool need_pbuffer, bool need_pixmap) {
120 
121  _egl_display = egl_pipe->get_egl_display();
122  _context = 0;
123  _fbconfig = 0;
124  _fbprops.clear();
125 
126  int attrib_list[] = {
127 #if defined(OPENGLES_1)
128  EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
129 #elif defined(OPENGLES_2)
130  EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
131 #else
132  EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
133 #endif
134  EGL_SURFACE_TYPE, need_window ? EGL_WINDOW_BIT : EGL_DONT_CARE,
135  EGL_NONE
136  };
137 
138  // First get the number of matching configurations, so we know how much
139  // memory to allocate.
140  int num_configs = 0, returned_configs;
141  if (!eglChooseConfig(_egl_display, attrib_list, nullptr, num_configs, &returned_configs) || returned_configs <= 0) {
142  egldisplay_cat.error() << "eglChooseConfig failed: "
143  << get_egl_error_string(eglGetError()) << "\n";
144  return;
145  }
146 
147  num_configs = returned_configs;
148  EGLConfig *configs = new EGLConfig[num_configs];
149 
150  if (!eglChooseConfig(_egl_display, attrib_list, configs, num_configs, &returned_configs) || returned_configs <= 0) {
151  egldisplay_cat.error() << "eglChooseConfig failed: "
152  << get_egl_error_string(eglGetError()) << "\n";
153  delete[] configs;
154  return;
155  }
156 
157  int best_quality = 0;
158  int best_result = 0;
159  FrameBufferProperties best_props;
160 
161  for (int i = 0; i < num_configs; ++i) {
162  FrameBufferProperties fbprops;
163  bool pbuffer_supported, pixmap_supported, slow;
164  get_properties(fbprops, pbuffer_supported, pixmap_supported,
165  slow, configs[i]);
166  // We're not protecting this code by an is_debug() check, because if we
167  // do, some weird compiler bug appears and somehow makes the quality
168  // always 0.
169  const char *pbuffertext = pbuffer_supported ? " (pbuffer)" : "";
170  const char *pixmaptext = pixmap_supported ? " (pixmap)" : "";
171  const char *slowtext = slow ? " (slow)" : "";
172  egldisplay_cat.debug()
173  << i << ": " << fbprops << pbuffertext << pixmaptext << slowtext << "\n";
174  int quality = fbprops.get_quality(properties);
175  if ((quality > 0)&&(slow)) quality -= 10000000;
176 
177  if (need_pbuffer && !pbuffer_supported) {
178  continue;
179  }
180  if (need_pixmap && !pixmap_supported) {
181  continue;
182  }
183 
184  if (quality > best_quality) {
185  best_quality = quality;
186  best_result = i;
187  best_props = fbprops;
188  }
189  }
190 #ifdef USE_X11
191  X11_Display *display = egl_pipe->get_display();
192  if (display) {
193  int screen = egl_pipe->get_screen();
194  int depth = DefaultDepth(display, screen);
195  _visual = new XVisualInfo;
196  XMatchVisualInfo(display, screen, depth, TrueColor, _visual);
197  }
198 #endif
199 
200  if (best_quality > 0) {
201  egldisplay_cat.debug()
202  << "Chosen config " << best_result << ": " << best_props << "\n";
203  _fbconfig = configs[best_result];
204 
205  EGLint attribs[32];
206  int n = 0;
207 
208 #ifdef OPENGLES_2
209  attribs[n++] = EGL_CONTEXT_CLIENT_VERSION;
210  attribs[n++] = 2;
211 #elif defined(OPENGLES_1)
212 #else // Regular OpenGL
213  if (gl_version.get_num_words() > 0) {
214  attribs[n++] = EGL_CONTEXT_MAJOR_VERSION;
215  attribs[n++] = gl_version[0];
216  if (gl_version.get_num_words() > 1) {
217  attribs[n++] = EGL_CONTEXT_MINOR_VERSION;
218  attribs[n++] = gl_version[1];
219  }
220  }
221  if (gl_debug) {
222  attribs[n++] = EGL_CONTEXT_OPENGL_DEBUG;
223  attribs[n++] = EGL_TRUE;
224  }
225  if (gl_forward_compatible) {
226  attribs[n++] = EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE;
227  attribs[n++] = EGL_TRUE;
228  }
229 #endif
230  attribs[n] = EGL_NONE;
231 
232  _context = eglCreateContext(_egl_display, _fbconfig, _share_context, (n > 0) ? attribs : nullptr);
233 
234  int err = eglGetError();
235  if (_context && err == EGL_SUCCESS) {
236 #ifdef USE_X11
237  if (!display || _visual)
238 #endif
239  {
240  // This is set during window creation, but for now we have to pretend
241  // that we can honor the request, if we support the extension.
242  if (properties.get_srgb_color()) {
243  const char *extensions = eglQueryString(_egl_display, EGL_EXTENSIONS);
244  if (extensions != nullptr) {
245  vector_string tokens;
246  extract_words(extensions, tokens);
247 
248  if (std::find(tokens.begin(), tokens.end(), "EGL_KHR_gl_colorspace") != tokens.end()) {
249  best_props.set_srgb_color(true);
250  }
251  }
252  }
253 
254  _fbprops = best_props;
255  delete[] configs;
256  return;
257  }
258  }
259  // This really shouldn't happen, so I'm not too careful about cleanup.
260  egldisplay_cat.error()
261  << "Could not create EGL context!\n"
262  << get_egl_error_string(err) << "\n";
263  _fbconfig = 0;
264  _context = 0;
265 #ifdef USE_X11
266  _visual = 0;
267 #endif
268  }
269 
270  egldisplay_cat.error() <<
271  "Could not find a usable pixel format.\n";
272 
273  delete[] configs;
274 }
275 
276 /**
277  * Resets all internal state as if the gsg were newly created.
278  */
280 reset() {
281  BaseGraphicsStateGuardian::reset();
282 
283  if (_gl_renderer == "Software Rasterizer") {
284  _fbprops.set_force_software(1);
285  _fbprops.set_force_hardware(0);
286  }
287 }
288 
289 /**
290  * Returns true if the runtime GLX version number is at least the indicated
291  * value, false otherwise.
292  */
294 egl_is_at_least_version(int major_version, int minor_version) const {
295  if (_egl_version_major < major_version) {
296  return false;
297  }
298  if (_egl_version_minor < minor_version) {
299  return false;
300  }
301  return true;
302 }
303 
304 /**
305  * Calls glFlush().
306  */
307 void eglGraphicsStateGuardian::
308 gl_flush() const {
309 #ifdef USE_X11
310  // This call requires synchronization with X.
311  LightReMutexHolder holder(eglGraphicsPipe::_x_mutex);
312 #endif
313  BaseGraphicsStateGuardian::gl_flush();
314 }
315 
316 /**
317  * Returns the result of glGetError().
318  */
319 GLenum eglGraphicsStateGuardian::
320 gl_get_error() const {
321 #ifdef USE_X11
322  // This call requires synchronization with X.
323  LightReMutexHolder holder(eglGraphicsPipe::_x_mutex);
324 #endif
325  return BaseGraphicsStateGuardian::gl_get_error();
326 }
327 
328 /**
329  * Queries the runtime version of OpenGL in use.
330  */
331 void eglGraphicsStateGuardian::
332 query_gl_version() {
333  BaseGraphicsStateGuardian::query_gl_version();
334 
335  // Calling eglInitialize on an already-initialized display will just provide
336  // us the version numbers.
337  if (!eglInitialize(_egl_display, &_egl_version_major, &_egl_version_minor)) {
338  egldisplay_cat.error() << "Failed to get EGL version number: "
339  << get_egl_error_string(eglGetError()) << "\n";
340  }
341 
342  // We output to glesgsg_cat instead of egldisplay_cat, since this is where
343  // the GL version has been output, and it's nice to see the two of these
344  // together.
345 #ifdef OPENGLES_2
346  if (gles2gsg_cat.is_debug()) {
347  gles2gsg_cat.debug()
348 #elif defined(OPENGLES_1)
349  if (glesgsg_cat.is_debug()) {
350  glesgsg_cat.debug()
351 #else
352  if (glgsg_cat.is_debug()) {
353  glgsg_cat.debug()
354 #endif
355  << "EGL_VERSION = " << _egl_version_major << "." << _egl_version_minor
356  << "\n";
357  }
358 }
359 
360 /**
361  * This may be redefined by a derived class (e.g. glx or wgl) to get whatever
362  * further extensions strings may be appropriate to that interface, in
363  * addition to the GL extension strings return by glGetString().
364  */
365 void eglGraphicsStateGuardian::
366 get_extra_extensions() {
367  save_extensions(eglQueryString(_egl_display, EGL_EXTENSIONS));
368 }
369 
370 /**
371  * Returns the pointer to the GL extension function with the indicated name.
372  * It is the responsibility of the caller to ensure that the required
373  * extension is defined in the OpenGL runtime prior to calling this; it is an
374  * error to call this for a function that is not defined.
375  */
376 void *eglGraphicsStateGuardian::
377 do_get_extension_func(const char *name) {
378  return (void *)eglGetProcAddress(name);
379 }
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
void set_rgba_bits(int r, int g, int b, int a)
Convenience method for setting the red, green, blue and alpha bits in one go.
void clear()
Unsets all properties that have been specified so far, and resets the FrameBufferProperties structure...
int get_quality(const FrameBufferProperties &reqs) const
Assumes that these properties are a description of a window.
This class is the main interface to controlling the render process.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
Similar to MutexHolder, but for a light reentrant mutex.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
This graphics pipe represents the interface for creating OpenGL ES graphics windows on an X-based (e....
A tiny specialization on GLESGraphicsStateGuardian to add some egl-specific information.
virtual void reset()
Resets all internal state as if the gsg were newly created.
bool egl_is_at_least_version(int major_version, int minor_version) const
Returns true if the runtime GLX version number is at least the indicated value, false otherwise.
void choose_pixel_format(const FrameBufferProperties &properties, eglGraphicsPipe *egl_pipe, bool need_window, bool need_pbuffer, bool need_pixmap)
Selects a visual or fbconfig for all the windows and buffers that use this gsg.
void get_properties(FrameBufferProperties &properties, bool &pbuffer_supported, bool &pixmap_supported, bool &slow, EGLConfig config)
Gets the FrameBufferProperties to match the indicated config.
const std::string get_egl_error_string(int error)
Returns the given EGL error as string.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int extract_words(const string &str, vector_string &words)
Divides the string into a number of words according to whitespace.