render_system.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011, Willow Garage, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Willow Garage, Inc. nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <QMetaType>
31 
32 // This is required for QT_MAC_USE_COCOA to be set
33 #include <QtCore/qglobal.h>
34 
35 #include <QMoveEvent>
36 
37 #if !defined(Q_OS_MAC) && !defined(Q_OS_WIN)
38 #include <X11/Xlib.h>
39 #include <X11/Xutil.h>
40 #include <GL/glx.h>
41 #endif
42 
43 // X.h #defines CursorShape to be "0". Qt uses CursorShape in normal
44 // C++ way. This wasn't an issue until ogre_logging.h (below)
45 // introduced a #include of <QString>.
46 #ifdef CursorShape
47 #undef CursorShape
48 #endif
49 
50 #include <ros/package.h> // This dependency should be moved out of here, it is just used for a search path.
51 #include <ros/console.h>
52 
53 #include <OgreRenderWindow.h>
54 #include <OgreSceneManager.h>
55 #if ((OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 9) || OGRE_VERSION_MAJOR >= 2)
56 #include <Overlay/OgreOverlaySystem.h>
57 #endif
58 
59 #include "rviz/env_config.h"
61 
63 
64 #include <QMessageBox>
65 
66 namespace rviz
67 {
68 RenderSystem* RenderSystem::instance_ = nullptr;
72 
74 {
75  if (instance_ == nullptr)
76  {
77  instance_ = new RenderSystem();
78  }
79  return instance_;
80 }
81 
83 {
84  force_gl_version_ = version;
85  ROS_INFO_STREAM("Forcing OpenGl version " << (float)version / 100.0 << ".");
86 }
87 
89 {
90  use_anti_aliasing_ = false;
91  ROS_INFO("Disabling Anti-Aliasing");
92 }
93 
95 {
96  force_no_stereo_ = true;
97  ROS_INFO("Forcing Stereo OFF");
98 }
99 
101 {
103 
104  std::string rviz_path = ros::package::getPath(ROS_PACKAGE_NAME);
105 
107  ogre_root_ = new Ogre::Root(rviz_path + "/ogre_media/plugins.cfg");
108 #if ((OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 9) || OGRE_VERSION_MAJOR >= 2)
109  ogre_overlay_system_ = new Ogre::OverlaySystem();
110 #endif
111  loadOgrePlugins();
113  ogre_root_->initialise(false);
115  detectGlVersion();
116  setupResources();
117  Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
118 }
119 
120 void RenderSystem::prepareOverlays(Ogre::SceneManager* scene_manager)
121 {
122 #if ((OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 9) || OGRE_VERSION_MAJOR >= 2)
124  scene_manager->addRenderQueueListener(ogre_overlay_system_);
125 #endif
126 }
127 
129 {
130 #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
131  dummy_window_id_ = 0;
132 #else
133  Display* display = XOpenDisplay(nullptr);
134 
135  if (display == nullptr)
136  {
137  ROS_WARN("$DISPLAY is invalid, falling back on default :0");
138  display = XOpenDisplay(":0");
139 
140  if (display == nullptr)
141  {
142  ROS_FATAL("Can't open default or :0 display. Try setting DISPLAY environment variable.");
143  throw std::runtime_error("Can't open default or :0 display!\n");
144  }
145  }
146 
147  int screen = DefaultScreen(display);
148 
149  int attribList[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 16, GLX_STENCIL_SIZE, 8, None};
150 
151  XVisualInfo* visual = glXChooseVisual(display, screen, (int*)attribList);
152 
153  dummy_window_id_ = XCreateSimpleWindow(display, RootWindow(display, screen), 0, 0, 1, 1, 0, 0, 0);
154 
155  GLXContext context = glXCreateContext(display, visual, nullptr, 1);
156 
157  glXMakeCurrent(display, dummy_window_id_, context);
158 #endif
159 }
160 
162 {
163  std::string plugin_prefix = get_ogre_plugin_path() + "/";
164 #ifdef Q_OS_MAC
165  plugin_prefix += "lib";
166 #endif
167  ogre_root_->loadPlugin(plugin_prefix + "RenderSystem_GL");
168  ogre_root_->loadPlugin(plugin_prefix + "Plugin_OctreeSceneManager");
169  ogre_root_->loadPlugin(plugin_prefix + "Plugin_ParticleFX");
170 }
171 
173 {
174  bool mesa_workaround = false;
175  if (force_gl_version_)
176  {
178  }
179  else
180  {
181  Ogre::RenderSystem* renderSys = ogre_root_->getRenderSystem();
182  const Ogre::RenderSystemCapabilities* caps = renderSys->createRenderSystemCapabilities();
183  ROS_INFO("OpenGL device: %s", caps->getDeviceName().c_str());
184  int major = caps->getDriverVersion().major;
185  int minor = caps->getDriverVersion().minor;
186  gl_version_ = major * 100 + minor * 10;
187 
188  std::string gl_version_string = (const char*)glGetString(GL_VERSION);
189  // The "Mesa 2" string is intended to match "Mesa 20.", "Mesa 21." and so on
190  mesa_workaround = gl_version_string.find("Mesa 2") != std::string::npos && gl_version_ >= 320;
191  }
192 
193  switch (gl_version_)
194  {
195  case 200:
196  glsl_version_ = 110;
197  break;
198  case 210:
199  glsl_version_ = 120;
200  break;
201  case 300:
202  glsl_version_ = 130;
203  break;
204  case 310:
205  glsl_version_ = 140;
206  break;
207  case 320:
208  glsl_version_ = 150;
209  break;
210  default:
211  if (gl_version_ > 320)
212  {
214  }
215  else
216  {
217  glsl_version_ = 0;
218  }
219  break;
220  }
221  if (mesa_workaround)
222  { // https://github.com/ros-visualization/rviz/issues/1508
223  ROS_INFO("OpenGl version: %.1f (GLSL %.1f) limited to GLSL 1.4 on Mesa system.",
224  (float)gl_version_ / 100.0, (float)glsl_version_ / 100.0);
225 
226  gl_version_ = 310;
227  glsl_version_ = 140;
228  return;
229  }
230  ROS_INFO("OpenGl version: %.1f (GLSL %.1f).", (float)gl_version_ / 100.0, (float)glsl_version_ / 100.0);
231 }
232 
234 {
235  Ogre::RenderSystem* renderSys;
236  const Ogre::RenderSystemList* rsList;
237 
238 // Get the list of available renderers.
239 #if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR == 6
240  rsList = ogre_root_->getAvailableRenderers();
241 #else
242  rsList = &(ogre_root_->getAvailableRenderers());
243 #endif
244 
245  // Look for the OpenGL one, which we require.
246  renderSys = nullptr;
247  for (unsigned int i = 0; i < rsList->size(); i++)
248  {
249  renderSys = rsList->at(i);
250  if (renderSys->getName().compare("OpenGL Rendering Subsystem") == 0)
251  {
252  break;
253  }
254  }
255 
256  if (renderSys == nullptr)
257  {
258  throw std::runtime_error("Could not find the opengl rendering subsystem!\n");
259  }
260 
261  // We operate in windowed mode
262  renderSys->setConfigOption("Full Screen", "No");
263 
269  // renderSys->setConfigOption("RTT Preferred Mode", "FBO");
270 
271  // Set the Full Screen Anti-Aliasing factor.
272  if (use_anti_aliasing_)
273  {
274  renderSys->setConfigOption("FSAA", "4");
275  }
276 
277  ogre_root_->setRenderSystem(renderSys);
278 }
279 
281 {
282  std::string rviz_path = ros::package::getPath(ROS_PACKAGE_NAME);
283  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(rviz_path + "/ogre_media", "FileSystem",
285  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(rviz_path + "/ogre_media/textures",
286  "FileSystem", ROS_PACKAGE_NAME);
287  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(rviz_path + "/ogre_media/fonts",
288  "FileSystem", ROS_PACKAGE_NAME);
289  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(rviz_path + "/ogre_media/models",
290  "FileSystem", ROS_PACKAGE_NAME);
291  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(rviz_path + "/ogre_media/materials",
292  "FileSystem", ROS_PACKAGE_NAME);
293  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
294  rviz_path + "/ogre_media/materials/scripts", "FileSystem", ROS_PACKAGE_NAME);
295  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
296  rviz_path + "/ogre_media/materials/glsl120", "FileSystem", ROS_PACKAGE_NAME);
297  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
298  rviz_path + "/ogre_media/materials/glsl120/nogp", "FileSystem", ROS_PACKAGE_NAME);
299  // Add resources that depend on a specific glsl version.
300  // Unfortunately, Ogre doesn't have a notion of glsl versions so we can't go
301  // the 'official' way of defining multiple schemes per material and let Ogre decide which one to use.
302  if (getGlslVersion() >= 150)
303  {
304  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
305  rviz_path + "/ogre_media/materials/glsl150", "FileSystem", ROS_PACKAGE_NAME);
306  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
307  rviz_path + "/ogre_media/materials/scripts150", "FileSystem", ROS_PACKAGE_NAME);
308  }
309  else if (getGlslVersion() >= 120)
310  {
311  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
312  rviz_path + "/ogre_media/materials/scripts120", "FileSystem", ROS_PACKAGE_NAME);
313  }
314  else
315  {
316  std::string s = "Your graphics driver does not support OpenGL 2.1. Please enable software rendering "
317  "before running RViz (e.g. type 'export LIBGL_ALWAYS_SOFTWARE=1').";
318  QMessageBox msgBox;
319  msgBox.setText(s.c_str());
320  msgBox.exec();
321  throw std::runtime_error(s);
322  }
323 
324  // Add paths exported to the "media_export" package.
325  std::vector<std::string> media_paths;
326  ros::package::getPlugins("media_export", "ogre_media_path", media_paths);
327  std::string delim(":");
328  for (std::vector<std::string>::iterator iter = media_paths.begin(); iter != media_paths.end(); ++iter)
329  {
330  if (!iter->empty())
331  {
332  std::string path;
333  int pos1 = 0;
334  int pos2 = iter->find(delim);
335  while (pos2 != (int)std::string::npos)
336  {
337  path = iter->substr(pos1, pos2 - pos1);
338  ROS_DEBUG("adding resource location: '%s'\n", path.c_str());
339  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(path, "FileSystem",
341  pos1 = pos2 + 1;
342  pos2 = iter->find(delim, pos2 + 1);
343  }
344  path = iter->substr(pos1, iter->size() - pos1);
345  ROS_DEBUG("adding resource location: '%s'\n", path.c_str());
346  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(path, "FileSystem",
348  }
349  }
350 }
351 
352 // On Intel graphics chips under X11, there sometimes comes a
353 // BadDrawable error during Ogre render window creation. It is not
354 // consistent, happens sometimes but not always. Underlying problem
355 // seems to be a driver bug. My workaround here is to notice when
356 // that specific BadDrawable error happens on request 136 minor 3
357 // (which is what the problem looks like when it happens) and just try
358 // over and over again until it works (or until 100 failures, which
359 // makes it seem like it is a different bug).
360 static bool x_baddrawable_error = false;
361 #ifdef Q_WS_X11
362 static int (*old_error_handler)(Display*, XErrorEvent*);
363 int checkBadDrawable(Display* display, XErrorEvent* error)
364 {
365  if (error->error_code == BadDrawable && error->request_code == 136 && error->minor_code == 3)
366  {
367  x_baddrawable_error = true;
368  return 0;
369  }
370  else
371  {
372  // If the error does not exactly match the one from the driver bug,
373  // handle it the normal way so we see it.
374  return old_error_handler(display, error);
375  }
376 }
377 #endif // Q_WS_X11
378 
379 Ogre::RenderWindow* RenderSystem::makeRenderWindow(WindowIDType window_id,
380  unsigned int width,
381  unsigned int height,
382  double pixel_ratio)
383 {
384  static int windowCounter = 0; // Every RenderWindow needs a unique name, oy.
385 
386  Ogre::NameValuePairList params;
387  Ogre::RenderWindow* window = nullptr;
388 
389  params["externalWindowHandle"] = Ogre::StringConverter::toString(window_id);
390  params["parentWindowHandle"] = Ogre::StringConverter::toString(window_id);
391 
392  params["externalGLControl"] = true;
393 
394  // Enable antialiasing
395  if (use_anti_aliasing_)
396  {
397  params["FSAA"] = "4";
398  }
399 
400 // Set the macAPI for Ogre based on the Qt implementation
401 #if defined(Q_OS_MAC)
402  params["macAPI"] = "cocoa";
403  params["macAPICocoaUseNSView"] = "true";
404 #endif
405  params["contentScalingFactor"] = std::to_string(pixel_ratio);
406 
407  std::ostringstream stream;
408  stream << "OgreWindow(" << windowCounter++ << ")";
409 
410 
411 // don't bother trying stereo if Ogre does not support it.
412 #if !OGRE_STEREO_ENABLE
413  force_no_stereo_ = true;
414 #endif
415 
416  // attempt to create a stereo window
417  bool is_stereo = false;
418  if (!force_no_stereo_)
419  {
420  params["stereoMode"] = "Frame Sequential";
421  window = tryMakeRenderWindow(stream.str(), width, height, &params, 100);
422  params.erase("stereoMode");
423 
424  if (window)
425  {
426 #if OGRE_STEREO_ENABLE
427  is_stereo = window->isStereoEnabled();
428 #endif
429  if (!is_stereo)
430  {
431  // Created a non-stereo window. Discard it and try again (below)
432  // without the stereo parameter.
433  ogre_root_->detachRenderTarget(window);
434  window->destroy();
435  window = nullptr;
436  stream << "x";
437  is_stereo = false;
438  }
439  }
440  }
441 
442  if (window == nullptr)
443  {
444  window = tryMakeRenderWindow(stream.str(), width, height, &params, 100);
445  }
446 
447  if (window == nullptr)
448  {
449  ROS_ERROR("Unable to create the rendering window after 100 tries.");
450  assert(false);
451  }
452 
453  if (window)
454  {
455  window->setActive(true);
456  // window->setVisible(true);
457  window->setAutoUpdated(false);
458  }
459 
460  stereo_supported_ = is_stereo;
461 
462  ROS_INFO_ONCE("Stereo is %s", stereo_supported_ ? "SUPPORTED" : "NOT SUPPORTED");
463 
464  return window;
465 }
466 
467 Ogre::RenderWindow* RenderSystem::tryMakeRenderWindow(const std::string& name,
468  unsigned int width,
469  unsigned int height,
470  const Ogre::NameValuePairList* params,
471  int max_attempts)
472 {
473  Ogre::RenderWindow* window = nullptr;
474  int attempts = 0;
475 
476 #ifdef Q_WS_X11
477  old_error_handler = XSetErrorHandler(&checkBadDrawable);
478 #endif
479 
480  while (window == nullptr && (attempts++) < max_attempts)
481  {
482  try
483  {
484  window = ogre_root_->createRenderWindow(name, width, height, false, params);
485 
486  // If the driver bug happened, tell Ogre we are done with that
487  // window and then try again.
488  if (x_baddrawable_error)
489  {
490  ogre_root_->detachRenderTarget(window);
491  window = nullptr;
492  x_baddrawable_error = false;
493  }
494  }
495  catch (const std::exception& ex)
496  {
497  std::cerr << "rviz::RenderSystem: error creating render window: " << ex.what() << std::endl;
498  window = nullptr;
499  }
500  }
501 
502 #ifdef Q_WS_X11
503  XSetErrorHandler(old_error_handler);
504 #endif
505 
506  if (window && attempts > 1)
507  {
508  ROS_INFO("Created render window after %d attempts.", attempts);
509  }
510 
511  return window;
512 }
513 
514 
515 } // end namespace rviz
#define ROS_FATAL(...)
static void configureLogging()
Configure the Ogre::LogManager to give the currently selected behavior. This must be called before Og...
#define ROS_INFO_ONCE(...)
static RenderSystem * instance_
std::string get_ogre_plugin_path()
static RenderSystem * get()
static int force_gl_version_
Ogre::RenderWindow * makeRenderWindow(WindowIDType window_id, unsigned int width, unsigned int height, double pixel_ratio=1.0)
XmlRpcServer s
static void forceGlVersion(int version)
Ogre::RenderWindow * tryMakeRenderWindow(const std::string &name, unsigned int width, unsigned int height, const Ogre::NameValuePairList *params, int max_attempts)
WindowIDType dummy_window_id_
unsigned long WindowIDType
Definition: render_system.h:51
Ogre::Root * ogre_root_
static void disableAntiAliasing()
#define ROS_WARN(...)
None
#define ROS_INFO(...)
void prepareOverlays(Ogre::SceneManager *scene_manager)
ROSLIB_DECL std::string getPath(const std::string &package_name)
ROSLIB_DECL void getPlugins(const std::string &package, const std::string &attribute, V_string &plugins, bool force_recrawl=false)
static bool force_no_stereo_
#define ROS_INFO_STREAM(args)
static bool x_baddrawable_error
Ogre::OverlaySystem * ogre_overlay_system_
static void forceNoStereo()
static bool use_anti_aliasing_
#define ROS_ERROR(...)
#define ROS_DEBUG(...)


rviz
Author(s): Dave Hershberger, David Gossow, Josh Faust
autogenerated on Sat May 27 2023 02:06:25