Panda3D
 All Classes Functions Variables Enumerations
eglGraphicsPipe.cxx
1 // Filename: eglGraphicsPipe.cxx
2 // Created by: pro-rsoft (21May09)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "eglGraphicsBuffer.h"
16 #include "eglGraphicsPipe.h"
17 #include "eglGraphicsPixmap.h"
18 #include "eglGraphicsWindow.h"
19 #include "eglGraphicsStateGuardian.h"
20 #include "config_egldisplay.h"
21 #include "frameBufferProperties.h"
22 
23 TypeHandle eglGraphicsPipe::_type_handle;
24 
25 bool eglGraphicsPipe::_error_handlers_installed = false;
26 eglGraphicsPipe::ErrorHandlerFunc *eglGraphicsPipe::_prev_error_handler;
27 eglGraphicsPipe::IOErrorHandlerFunc *eglGraphicsPipe::_prev_io_error_handler;
28 
29 LightReMutex eglGraphicsPipe::_x_mutex;
30 
31 ////////////////////////////////////////////////////////////////////
32 // Function: eglGraphicsPipe::Constructor
33 // Access: Public
34 // Description:
35 ////////////////////////////////////////////////////////////////////
36 eglGraphicsPipe::
37 eglGraphicsPipe(const string &display) {
38  string display_spec = display;
39  if (display_spec.empty()) {
40  display_spec = display_cfg;
41  }
42  if (display_spec.empty()) {
43  display_spec = ExecutionEnvironment::get_environment_variable("DISPLAY");
44  }
45  if (display_spec.empty()) {
46  display_spec = ":0.0";
47  }
48 
49  // The X docs say we should do this to get international character
50  // support from the keyboard.
51  setlocale(LC_ALL, "");
52 
53  // But it's important that we use the "C" locale for numeric
54  // formatting, since all of the internal Panda code assumes this--we
55  // need a decimal point to mean a decimal point.
56  setlocale(LC_NUMERIC, "C");
57 
58  _is_valid = false;
59  _supported_types = OT_window | OT_buffer | OT_texture_buffer;
60  _display = NULL;
61  _screen = 0;
62  _root = (X11_Window)NULL;
63  _im = (XIM)NULL;
64  _hidden_cursor = None;
65  _egl_display = NULL;
66 
67  install_error_handlers();
68 
69  _display = XOpenDisplay(display_spec.c_str());
70  if (!_display) {
71  egldisplay_cat.error()
72  << "Could not open display \"" << display_spec << "\".\n";
73  return;
74  }
75 
76  if (!XSupportsLocale()) {
77  egldisplay_cat.warning()
78  << "X does not support locale " << setlocale(LC_ALL, NULL) << "\n";
79  }
80  XSetLocaleModifiers("");
81 
82  _screen = DefaultScreen(_display);
83  _root = RootWindow(_display, _screen);
84  _display_width = DisplayWidth(_display, _screen);
85  _display_height = DisplayHeight(_display, _screen);
86  _is_valid = true;
87 
88  _egl_display = eglGetDisplay((NativeDisplayType) _display);
89  if (!eglInitialize(_egl_display, NULL, NULL)) {
90  egldisplay_cat.error()
91  << "Couldn't initialize the EGL display: "
92  << get_egl_error_string(eglGetError()) << "\n";
93  }
94 
95  if (!eglBindAPI(EGL_OPENGL_ES_API)) {
96  egldisplay_cat.error()
97  << "Couldn't bind EGL to the OpenGL ES API: "
98  << get_egl_error_string(eglGetError()) << "\n";
99  }
100 
101  // Connect to an input method for supporting international text
102  // entry.
103  _im = XOpenIM(_display, NULL, NULL, NULL);
104  if (_im == (XIM)NULL) {
105  egldisplay_cat.warning()
106  << "Couldn't open input method.\n";
107  }
108 
109  // What styles does the current input method support?
110  /*
111  XIMStyles *im_supported_styles;
112  XGetIMValues(_im, XNQueryInputStyle, &im_supported_styles, NULL);
113 
114  for (int i = 0; i < im_supported_styles->count_styles; i++) {
115  XIMStyle style = im_supported_styles->supported_styles[i];
116  cerr << "style " << i << ". " << hex << style << dec << "\n";
117  }
118 
119  XFree(im_supported_styles);
120  */
121 
122  // Get some X atom numbers.
123  _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", false);
124  _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
125  _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
126  _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false);
127  _net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false);
128  _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
129  _net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false);
130  _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
131  _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
132  _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
133 }
134 
135 ////////////////////////////////////////////////////////////////////
136 // Function: eglGraphicsPipe::Destructor
137 // Access: Public, Virtual
138 // Description:
139 ////////////////////////////////////////////////////////////////////
140 eglGraphicsPipe::
141 ~eglGraphicsPipe() {
142  release_hidden_cursor();
143  if (_im) {
144  XCloseIM(_im);
145  }
146  if (_display) {
147  XCloseDisplay(_display);
148  }
149  if (_egl_display) {
150  if (!eglTerminate(_egl_display)) {
151  egldisplay_cat.error() << "Failed to terminate EGL display: "
152  << get_egl_error_string(eglGetError()) << "\n";
153  }
154  }
155 }
156 
157 ////////////////////////////////////////////////////////////////////
158 // Function: eglGraphicsPipe::get_interface_name
159 // Access: Published, Virtual
160 // Description: Returns the name of the rendering interface
161 // associated with this GraphicsPipe. This is used to
162 // present to the user to allow him/her to choose
163 // between several possible GraphicsPipes available on a
164 // particular platform, so the name should be meaningful
165 // and unique for a given platform.
166 ////////////////////////////////////////////////////////////////////
167 string eglGraphicsPipe::
169  return "OpenGL ES";
170 }
171 
172 ////////////////////////////////////////////////////////////////////
173 // Function: eglGraphicsPipe::pipe_constructor
174 // Access: Public, Static
175 // Description: This function is passed to the GraphicsPipeSelection
176 // object to allow the user to make a default
177 // eglGraphicsPipe.
178 ////////////////////////////////////////////////////////////////////
180 pipe_constructor() {
181  return new eglGraphicsPipe;
182 }
183 
184 ////////////////////////////////////////////////////////////////////
185 // Function: eglGraphicsPipe::get_preferred_window_thread
186 // Access: Public, Virtual
187 // Description: Returns an indication of the thread in which this
188 // GraphicsPipe requires its window processing to be
189 // performed: typically either the app thread (e.g. X)
190 // or the draw thread (Windows).
191 ////////////////////////////////////////////////////////////////////
192 GraphicsPipe::PreferredWindowThread
194  // Actually, since we're creating the graphics context in
195  // open_window() now, it appears we need to ensure the open_window()
196  // call is performed in the draw thread for now, even though X wants
197  // all of its calls to be single-threaded.
198 
199  // This means that all X windows may have to be handled by the same
200  // draw thread, which we didn't intend (though the global _x_mutex
201  // may allow them to be technically served by different threads,
202  // even though the actual X calls will be serialized). There might
203  // be a better way.
204 
205  return PWT_draw;
206 }
207 
208 ////////////////////////////////////////////////////////////////////
209 // Function: eglGraphicsPipe::make_output
210 // Access: Protected, Virtual
211 // Description: Creates a new window on the pipe, if possible.
212 ////////////////////////////////////////////////////////////////////
214 make_output(const string &name,
215  const FrameBufferProperties &fb_prop,
216  const WindowProperties &win_prop,
217  int flags,
218  GraphicsEngine *engine,
220  GraphicsOutput *host,
221  int retry,
222  bool &precertify) {
223 
224  if (!_is_valid) {
225  return NULL;
226  }
227 
228  eglGraphicsStateGuardian *eglgsg = 0;
229  if (gsg != 0) {
230  DCAST_INTO_R(eglgsg, gsg, NULL);
231  }
232 
233  bool support_rtt;
234  support_rtt = false;
235  /*
236  Currently, no support for eglGraphicsBuffer render-to-texture.
237  if (eglgsg) {
238  support_rtt =
239  eglgsg -> get_supports_render_texture() &&
240  support_render_texture;
241  }
242  */
243 
244  // First thing to try: an eglGraphicsWindow
245 
246  if (retry == 0) {
247  if (((flags&BF_require_parasite)!=0)||
248  ((flags&BF_refuse_window)!=0)||
249  ((flags&BF_resizeable)!=0)||
250  ((flags&BF_size_track_host)!=0)||
251  ((flags&BF_rtt_cumulative)!=0)||
252  ((flags&BF_can_bind_color)!=0)||
253  ((flags&BF_can_bind_every)!=0)) {
254  return NULL;
255  }
256  return new eglGraphicsWindow(engine, this, name, fb_prop, win_prop,
257  flags, gsg, host);
258  }
259 
260  // Second thing to try: a GLES(2)GraphicsBuffer
261  if (retry == 1) {
262  if ((host==0)||
263  // (!gl_support_fbo)||
264  ((flags&BF_require_parasite)!=0)||
265  ((flags&BF_require_window)!=0)) {
266  return NULL;
267  }
268  // Early failure - if we are sure that this buffer WONT
269  // meet specs, we can bail out early.
270  if ((flags & BF_fb_props_optional)==0) {
271  if ((fb_prop.get_indexed_color() > 0)||
272  (fb_prop.get_back_buffers() > 0)||
273  (fb_prop.get_accum_bits() > 0)||
274  (fb_prop.get_multisamples() > 0)) {
275  return NULL;
276  }
277  }
278  // Early success - if we are sure that this buffer WILL
279  // meet specs, we can precertify it.
280  if ((eglgsg != 0) &&
281  (eglgsg->is_valid()) &&
282  (!eglgsg->needs_reset()) &&
283  (eglgsg->_supports_framebuffer_object) &&
284  (eglgsg->_glDrawBuffers != 0)&&
285  (fb_prop.is_basic())) {
286  precertify = true;
287  }
288 #ifdef OPENGLES_2
289  return new GLES2GraphicsBuffer(engine, this, name, fb_prop, win_prop,
290  flags, gsg, host);
291 #else
292  return new GLESGraphicsBuffer(engine, this, name, fb_prop, win_prop,
293  flags, gsg, host);
294 #endif
295  }
296 
297  // Third thing to try: a eglGraphicsBuffer
298  if (retry == 2) {
299  if (((flags&BF_require_parasite)!=0)||
300  ((flags&BF_require_window)!=0)||
301  ((flags&BF_resizeable)!=0)||
302  ((flags&BF_size_track_host)!=0)) {
303  return NULL;
304  }
305 
306  if (!support_rtt) {
307  if (((flags&BF_rtt_cumulative)!=0)||
308  ((flags&BF_can_bind_every)!=0)) {
309  // If we require Render-to-Texture, but can't be sure we
310  // support it, bail.
311  return NULL;
312  }
313  }
314 
315  return new eglGraphicsBuffer(engine, this, name, fb_prop, win_prop,
316  flags, gsg, host);
317  }
318 
319  // Fourth thing to try: an eglGraphicsPixmap.
320  if (retry == 3) {
321  if (((flags&BF_require_parasite)!=0)||
322  ((flags&BF_require_window)!=0)||
323  ((flags&BF_resizeable)!=0)||
324  ((flags&BF_size_track_host)!=0)) {
325  return NULL;
326  }
327 
328  if (((flags&BF_rtt_cumulative)!=0)||
329  ((flags&BF_can_bind_every)!=0)) {
330  return NULL;
331  }
332 
333  return new eglGraphicsPixmap(engine, this, name, fb_prop, win_prop,
334  flags, gsg, host);
335  }
336 
337  // Nothing else left to try.
338  return NULL;
339 }
340 
341 ////////////////////////////////////////////////////////////////////
342 // Function: eglGraphicsPipe::make_hidden_cursor
343 // Access: Private
344 // Description: Called once to make an invisible Cursor for return
345 // from get_hidden_cursor().
346 ////////////////////////////////////////////////////////////////////
347 void eglGraphicsPipe::
348 make_hidden_cursor() {
349  nassertv(_hidden_cursor == None);
350 
351  unsigned int x_size, y_size;
352  XQueryBestCursor(_display, _root, 1, 1, &x_size, &y_size);
353 
354  Pixmap empty = XCreatePixmap(_display, _root, x_size, y_size, 1);
355 
356  XColor black;
357  memset(&black, 0, sizeof(black));
358 
359  _hidden_cursor = XCreatePixmapCursor(_display, empty, empty,
360  &black, &black, x_size, y_size);
361  XFreePixmap(_display, empty);
362 }
363 
364 ////////////////////////////////////////////////////////////////////
365 // Function: eglGraphicsPipe::release_hidden_cursor
366 // Access: Private
367 // Description: Called once to release the invisible cursor created
368 // by make_hidden_cursor().
369 ////////////////////////////////////////////////////////////////////
370 void eglGraphicsPipe::
371 release_hidden_cursor() {
372  if (_hidden_cursor != None) {
373  XFreeCursor(_display, _hidden_cursor);
374  _hidden_cursor = None;
375  }
376 }
377 
378 ////////////////////////////////////////////////////////////////////
379 // Function: eglGraphicsPipe::install_error_handlers
380 // Access: Private, Static
381 // Description: Installs new Xlib error handler functions if this is
382 // the first time this function has been called. These
383 // error handler functions will attempt to reduce Xlib's
384 // annoying tendency to shut down the client at the
385 // first error. Unfortunately, it is difficult to play
386 // nice with the client if it has already installed its
387 // own error handlers.
388 ////////////////////////////////////////////////////////////////////
389 void eglGraphicsPipe::
390 install_error_handlers() {
391  if (_error_handlers_installed) {
392  return;
393  }
394 
395  _prev_error_handler = (ErrorHandlerFunc *)XSetErrorHandler(error_handler);
396  _prev_io_error_handler = (IOErrorHandlerFunc *)XSetIOErrorHandler(io_error_handler);
397  _error_handlers_installed = true;
398 }
399 
400 ////////////////////////////////////////////////////////////////////
401 // Function: eglGraphicsPipe::error_handler
402 // Access: Private, Static
403 // Description: This function is installed as the error handler for a
404 // non-fatal Xlib error.
405 ////////////////////////////////////////////////////////////////////
406 int eglGraphicsPipe::
407 error_handler(X11_Display *display, XErrorEvent *error) {
408  static const int msg_len = 80;
409  char msg[msg_len];
410  XGetErrorText(display, error->error_code, msg, msg_len);
411  egldisplay_cat.error()
412  << msg << "\n";
413 
414  if (x_error_abort) {
415  abort();
416  }
417 
418  // We return to allow the application to continue running, unlike
419  // the default X error handler which exits.
420  return 0;
421 }
422 
423 ////////////////////////////////////////////////////////////////////
424 // Function: eglGraphicsPipe::io_error_handler
425 // Access: Private, Static
426 // Description: This function is installed as the error handler for a
427 // fatal Xlib error.
428 ////////////////////////////////////////////////////////////////////
429 int eglGraphicsPipe::
430 io_error_handler(X11_Display *display) {
431  egldisplay_cat.fatal()
432  << "X fatal error on display " << (void *)display << "\n";
433 
434  // Unfortunately, we can't continue from this function, even if we
435  // promise never to use X again. We're supposed to terminate
436  // without returning, and if we do return, the caller will exit
437  // anyway. Sigh. Very poor design on X's part.
438  return 0;
439 }
A lightweight reentrant mutex.
Definition: lightReMutex.h:34
An interface to the egl system for managing GLES windows under X.
This graphics pipe represents the interface for creating OpenGL ES graphics windows on an X-based (e...
virtual string get_interface_name() const
Returns the name of the rendering interface associated with this GraphicsPipe.
virtual PreferredWindowThread get_preferred_window_thread() const
Returns an indication of the thread in which this GraphicsPipe requires its window processing to be p...
A container for the various kinds of properties we might ask to have on a graphics window before we o...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
Another offscreen buffer in the EGL environment.
static string get_environment_variable(const string &var)
Returns the definition of the indicated environment variable, or the empty string if the variable is ...
This is a base class for the various different classes that represent the result of a frame of render...
An offscreen buffer in the EGL environment.
Encapsulates all the communication with a particular instance of a given rendering backend...
A tiny specialization on GLESGraphicsStateGuardian to add some egl-specific information.
This class is the main interface to controlling the render process.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...