FlightGear next
fg_os_osgviewer.cxx
Go to the documentation of this file.
1// fg_os_osgviewer.cxx -- common functions for fg_os interface
2// implemented as an osgViewer
3//
4// Copyright (C) 2007 Tim Moore timoore@redhat.com
5// Copyright (C) 2007 Mathias Froehlich
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License as
9// published by the Free Software Foundation; either version 2 of the
10// License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15// General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21#ifdef __linux__
22#include <sched.h>
23#endif
24
25#include <config.h>
26
27#include <algorithm>
28#include <cstdlib>
29#include <iostream>
30#include <sstream>
31#include <string>
32
33#include <simgear/compiler.h>
34#include <simgear/debug/logstream.hxx>
35#include <simgear/props/props_io.hxx>
36#include <simgear/structure/exception.hxx>
37#include <simgear/scene/util/OsgIoCapture.hxx>
38
39#include <osg/Camera>
40#include <osg/GraphicsContext>
41#include <osg/Group>
42#include <osg/Matrixd>
43#include <osg/Notify>
44#include <osg/Version>
45#include <osg/View>
46#include <osg/Viewport>
47#include <osgViewer/GraphicsWindow>
48#include <osgViewer/Viewer>
49#include <osgViewer/ViewerEventHandlers>
50
51#include "CameraGroup.hxx"
52#include "FGEventHandler.hxx"
53#include "VRManager.hxx"
54#include "WindowBuilder.hxx"
56#include "renderer.hxx"
57#include <Main/fg_os.hxx>
58#include <Main/fg_props.hxx>
59#include <Main/globals.hxx>
61#include <Main/util.hxx>
62#include <Scenery/scenery.hxx>
63
64#if defined(SG_MAC)
65#include <GUI/CocoaHelpers.h>
66#endif
67
68#if defined(SG_WINDOWS)
69#include <process.h> // _getpid()
70#endif
71
72using namespace std;
73using namespace flightgear;
74using namespace osg;
75
76class NotifyLevelListener : public SGPropertyChangeListener
77{
78public:
79 void valueChanged(SGPropertyNode* node)
80 {
81 osg::NotifySeverity severity = osg::getNotifyLevel();
82 string val = simgear::strutils::lowercase(node->getStringValue());
83
84 if (val == "fatal") {
85 severity = osg::FATAL;
86 } else if (val == "warn") {
87 severity = osg::WARN;
88 } else if (val == "notice") {
89 severity = osg::NOTICE;
90 } else if (val == "info") {
91 severity = osg::INFO;
92 } else if ((val == "debug") || (val == "debug-info")) {
93 severity = osg::DEBUG_INFO;
94 }
95
96 osg::setNotifyLevel(severity);
97 }
98};
99
101{
102}
103
105{
106 osg::setNotifyHandler(new SGNotifyHandler);
107
108 auto composite_viewer = dynamic_cast<osgViewer::CompositeViewer*>(
109 globals->get_renderer()->getViewerBase());
110 if (composite_viewer) {
111 osgViewer::ViewerBase* viewer = globals->get_renderer()->getViewerBase();
112 osgViewer::View* view = new osgViewer::View;
113 view->setFrameStamp(composite_viewer->getFrameStamp());
114 globals->get_renderer()->setView(view);
115 assert(globals->get_renderer()->getView() == view);
116 view->setDatabasePager(FGScenery::getPagerSingleton());
117
118 // https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg29820.html
119 view->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
120 // XXX: Creating a new context ID makes FG segfault/double free at exit.
121 // Comment this for now...
122 // osg::GraphicsContext::createNewContextID();
123
124 // Disable the main camera, use slaves instead
125 view->getCamera()->setGraphicsContext(nullptr);
126
127 std::string mode;
128 mode = fgGetString("/sim/rendering/multithreading-mode", "SingleThreaded");
129 SG_LOG(SG_VIEW, SG_INFO, "mode=" << mode);
130 if (mode == "AutomaticSelection")
131 viewer->setThreadingModel(osgViewer::Viewer::AutomaticSelection);
132 else if (mode == "CullDrawThreadPerContext")
133 viewer->setThreadingModel(osgViewer::Viewer::CullDrawThreadPerContext);
134 else if (mode == "DrawThreadPerContext")
135 viewer->setThreadingModel(osgViewer::Viewer::DrawThreadPerContext);
136 else if (mode == "CullThreadPerCameraDrawThreadPerContext")
137 viewer->setThreadingModel(osgViewer::Viewer::CullThreadPerCameraDrawThreadPerContext);
138 else
139 viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
140
143
144 FGEventHandler* manipulator = globals->get_renderer()->getEventHandler();
146 if (wsa->windows.size() != 1) {
147 manipulator->setResizable(false);
148 }
149 view->getCamera()->setProjectionResizePolicy(osg::Camera::FIXED);
150 view->addEventHandler(manipulator);
151 // Let FG handle the escape key with a confirmation
152 viewer->setKeyEventSetsDone(0);
153 // The viewer won't start without some root.
154 view->setSceneData(new osg::Group);
155 }
156}
157
159
160// Getter/Setter to work around lack of unsigned int properties. Note that we have a minimum of 1 DB thread as otherwise
161// nothing will be loaded. We also force the number of HTTP threads to 0, as we don't use them.
162inline int getNumDatabaseThreads() { return DisplaySettings::instance()->getNumOfDatabaseThreadsHint(); }
163inline void setNumDatabaseThreads(int threads) { DisplaySettings::instance()->setNumOfDatabaseThreadsHint(max(threads, 1)); DisplaySettings::instance()->setNumOfHttpDatabaseThreadsHint(0); }
164
166{
167 SGPropertyNode* osgLevel = fgGetNode("/sim/rendering/osg-notify-level", true);
168 simTotalHostTime = fgGetNode("/sim/rendering/sim-host-total-ms", true);
169 simHost = fgGetNode("/sim/rendering/sim-host-avg-ms", true);
170 simFrameCount = fgGetNode("/sim/rendering/sim-frame-count", true);
171 simFrameResetCount = fgGetNode("/sim/rendering/sim-frame-count-reset", true);
172 frameWait = fgGetNode("/sim/time/frame-wait-ms", true);
173 simFrameResetCount->setBoolValue(false);
175 globals->addListenerToCleanup(l);
176 osgLevel->addChangeListener(l, true);
177
178 osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
179 if (guiCamera) {
180 Viewport* guiViewport = guiCamera->getViewport();
181 fgSetInt("/sim/startup/xsize", guiViewport->width());
182 fgSetInt("/sim/startup/ysize", guiViewport->height());
183 }
184
185 fgTie("/sim/rendering/database-pager/threads", &getNumDatabaseThreads, &setNumDatabaseThreads);
186
187#ifdef ENABLE_OSGXR
188 fgSetBool("/sim/vr/built", true);
189#else
190 fgSetBool("/sim/vr/built", false);
191#endif
192}
193
194
195static int status = 0;
196
197void fgOSExit(int code)
198{
199 FGRenderer* renderer = globals->get_renderer();
200 renderer->getViewerBase()->setDone(true);
201 renderer->getView()->getDatabasePager()->cancel();
202 status = code;
203
204 // otherwise we crash if OSG does logging during static destruction, eg
205 // GraphicsWindowX11, since OSG statics may have been created before the
206 // sglog static, despite our best efforts in boostrap.cxx
207 osg::setNotifyHandler(new osg::StandardNotifyHandler);
208}
209SGTimeStamp _lastUpdate;
210
211static void ShowAffinities()
212{
213#ifdef __linux__
214 char command[1024];
215 snprintf(command, sizeof(command), "for i in `ls /proc/%i/task/`; do taskset -p $i; done 1>&2", getpid());
216 SG_LOG(SG_VIEW, SG_ALERT, "Running: " << command);
217 system(command);
218#endif
219}
220
221#ifdef __linux__
222static std::ostream& operator<<(std::ostream& out, const cpu_set_t& mask)
223{
224 out << "0x";
225 unsigned char* mask2 = (unsigned char*)&mask;
226 for (unsigned i = 0; i < sizeof(mask); ++i) {
227 char buffer[8];
228 snprintf(buffer, sizeof(buffer), "%02x", (unsigned)mask2[i]);
229 out << buffer;
230 }
231 return out;
232}
233#endif
234
235/* Listen to /sim/affinity-control and, on Linux only, responds to
236value='clear' and 'revert':
237
238 'clear'
239 Stores current affinities for all thread then resets all affinities so
240 that all threads can run on any cpu core.
241 'revert'
242 Restores thread affinities stored from previous 'clear'.
243*/
244struct AffinityControl : SGPropertyChangeListener {
246 {
247 m_node = globals->get_props()->getNode("/sim/affinity-control", true /*create*/);
248 m_node->addChangeListener(this);
249 }
250 void valueChanged(SGPropertyNode* node) override
251 {
252#ifdef __linux__
253 std::string s = m_node->getStringValue();
254 if (s == m_state) {
255 SG_LOG(SG_VIEW, SG_ALERT, "Ignoring m_node=" << s << " because same as m_state.");
256 } else if (s == "clear") {
257 char buffer[64];
258 snprintf(buffer, sizeof(buffer), "/proc/%i/task", getpid());
259 SGPath path(buffer);
260 simgear::Dir dir(path);
261 m_thread_masks.clear();
262 simgear::PathList pids = dir.children(
263 simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
264 for (SGPath path : pids) {
265 std::string leaf = path.file();
266 int pid = atoi(leaf.c_str());
267 cpu_set_t mask;
268 int e = sched_getaffinity(pid, sizeof(mask), &mask);
269 SG_LOG(SG_VIEW, SG_ALERT, "Called sched_getaffinity()"
270 << " pid=" << pid << " => e=" << e << " mask=" << mask);
271 if (!e) {
272 m_thread_masks[pid] = mask;
273 memset(&mask, 255, sizeof(mask));
274 e = sched_setaffinity(pid, sizeof(mask), &mask);
275 SG_LOG(SG_VIEW, SG_ALERT, "Called sched_setaffinity()"
276 << " pid=" << pid << " => e=" << e << " mask=" << mask);
277 //assert(!e);
278 }
279 }
280 m_state = s;
281 } else if (s == "revert") {
282 for (auto it : m_thread_masks) {
283 pid_t pid = it.first;
284 cpu_set_t mask = it.second;
285 int e = sched_setaffinity(pid, sizeof(mask), &mask);
286 SG_LOG(SG_VIEW, SG_ALERT, "Called sched_setaffinity()"
287 << " pid=" << pid << " => e=" << e << " mask=" << mask);
288 //assert(!e);
289 }
290 m_thread_masks.clear();
291 m_state = s;
292 } else {
293 SG_LOG(SG_VIEW, SG_ALERT, "Unrecognised m_node=" << s);
294 }
295#endif
296 }
297 SGPropertyNode_ptr m_node;
298 std::string m_state;
299#ifdef __linux__
300 std::map<int, cpu_set_t> m_thread_masks;
301#endif
302};
303
304
306{
307 AffinityControl affinity_control;
308 osgViewer::ViewerBase* viewer_base = globals->get_renderer()->getViewerBase();
309 viewer_base->setReleaseContextAtEndOfFrameHint(false);
310 if (!viewer_base->isRealized()) {
311 viewer_base->realize();
312 std::string affinity = fgGetString("/sim/thread-cpu-affinity");
313 if (affinity != "") {
315 if (affinity == "osg") {
316 SG_LOG(SG_VIEW, SG_INFO, "Resetting affinity of current thread getpid()=" << getpid());
317 OpenThreads::Affinity affinity;
318 OpenThreads::SetProcessorAffinityOfCurrentThread(affinity);
320 }
321 }
322 }
323
324 while (!viewer_base->done()) {
325 fgIdleHandler idleFunc = globals->get_renderer()->getEventHandler()->getIdleHandler();
326 if (idleFunc) {
327 _lastUpdate.stamp();
328 (*idleFunc)();
329 if (fgGetBool("/sim/position-finalized", false)) {
331 int curFrameCount = simFrameCount->getIntValue();
332 double totalSimTime = simTotalHostTime->getDoubleValue();
333 if (simFrameResetCount->getBoolValue()) {
334 curFrameCount = 0;
335 totalSimTime = 0;
336 simFrameResetCount->setBoolValue(false);
337 }
338 double lastSimFrame_ms = _lastUpdate.elapsedMSec();
339 double idle_wait = 0;
340 if (frameWait)
341 idle_wait = frameWait->getDoubleValue();
342 if (lastSimFrame_ms > 0) {
343 totalSimTime += lastSimFrame_ms - idle_wait;
344 simTotalHostTime->setDoubleValue(totalSimTime);
345 curFrameCount++;
346 simFrameCount->setIntValue(curFrameCount);
347 simHost->setDoubleValue(totalSimTime / curFrameCount);
348 }
349 }
350 }
351 }
352 globals->get_renderer()->update();
353#ifdef ENABLE_OSGXR
354 VRManager::instance()->update();
355#endif
356 viewer_base->frame(globals->get_sim_time_sec());
357 }
358
359 flightgear::addSentryBreadcrumb("main loop exited", "info");
360 return status;
361}
362
364{
365 FGRenderer* r = globals->get_renderer();
366 if (!r || !r->getEventHandler()) { // happens during shutdown
367 return 0;
368 }
369
371}
372
373void fgWarpMouse(int x, int y)
374{
376}
377
378void fgOSInit(int* argc, char** argv)
379{
380 // stock OSG windows are not Hi-DPI aware
381 fgSetDouble("/sim/rendering/gui-pixel-ratio", 1.0);
382
383#if defined(SG_MAC)
385#endif
386
387 globals->set_renderer(new FGRenderer);
388 globals->get_renderer()->init();
390}
391
393{
394 // reset the cursor before we close the window
395
397
398 if (globals && globals->get_renderer()) {
399 osgViewer::ViewerBase* viewer_base = globals->get_renderer()->getViewerBase();
400 if (viewer_base) {
401 // https://code.google.com/p/flightgear-bugs/issues/detail?id=1291
402 // https://sourceforge.net/p/flightgear/codetickets/1830/
403 // explicitly stop threading before we delete the renderer or
404 // viewMgr (which ultimately holds refs to the CameraGroup, and
405 // GraphicsContext)
406 viewer_base->stopThreading();
407 }
408 }
409#ifdef ENABLE_OSGXR
410 VRManager::instance()->destroyAndWait();
411#endif
413 flightgear::addSentryBreadcrumb("fgOSCloseWindow, clearing camera group", "info");
416}
417
419{
420 osgViewer::ViewerBase* viewer_base = globals->get_renderer()->getViewerBase();
421 std::vector<osgViewer::GraphicsWindow*> windows;
422 viewer_base->getWindows(windows);
423
424 if (windows.empty())
425 return; // Huh?!?
426
427 /* Toggling window fullscreen is only supported for the main GUI window.
428 * The other windows should use fixed setup from the camera.xml file anyway. */
429 osgViewer::GraphicsWindow* window = windows[0];
430
431 osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
432 if (wsi == NULL) {
433 SG_LOG(SG_VIEW, SG_ALERT, "ERROR: No WindowSystemInterface available. Cannot toggle window fullscreen.");
434 return;
435 }
436
437 static int previous_x = 0;
438 static int previous_y = 0;
439 static int previous_width = 800;
440 static int previous_height = 600;
441
442 unsigned int screenWidth;
443 unsigned int screenHeight;
444 wsi->getScreenResolution(*(window->getTraits()), screenWidth, screenHeight);
445
446 int x;
447 int y;
448 int width;
449 int height;
450 window->getWindowRectangle(x, y, width, height);
451
452 /* Note: the simple "is window size == screen size" check to detect full screen state doesn't work with
453 * X screen servers in Xinerama mode, since the reported screen width (or height) exceeds the maximum width
454 * (or height) usable by a single window (Xserver automatically shrinks/moves the full screen window to fit a
455 * single display) - so we detect full screen mode using "WindowDecoration" state instead.
456 * "false" - even when a single window is display in fullscreen */
457 //bool isFullScreen = x == 0 && y == 0 && width == (int)screenWidth && height == (int)screenHeight;
458 bool isFullScreen = !window->getWindowDecoration();
459
460 SG_LOG(SG_VIEW, SG_DEBUG, "Toggling fullscreen. Previous window rectangle (" << x << ", " << y << ") x (" << width << ", " << height << "), fullscreen: " << isFullScreen << ", number of screens: " << wsi->getNumScreens());
461 if (isFullScreen) {
462 // limit x,y coordinates and window size to screen area
463 if (previous_x + previous_width > (int)screenWidth)
464 previous_x = 0;
465 if (previous_y + previous_height > (int)screenHeight)
466 previous_y = 0;
467
468 // disable fullscreen mode, restore previous window size/coordinates
469 x = previous_x;
470 y = previous_y;
471 width = previous_width;
472 height = previous_height;
473 } else {
474 // remember previous setting
475 previous_x = x;
476 previous_y = y;
477 previous_width = width;
478 previous_height = height;
479
480 // enable fullscreen mode, set new width/height
481 x = 0;
482 y = 0;
483 width = screenWidth;
484 height = screenHeight;
485 }
486
487 // set xsize/ysize properties to adapt GUI planes
488 fgSetInt("/sim/startup/xsize", width);
489 fgSetInt("/sim/startup/ysize", height);
490 fgSetBool("/sim/startup/fullscreen", !isFullScreen);
491
492 // reconfigure window
493 window->setWindowDecoration(isFullScreen);
494 window->setWindowRectangle(x, y, width, height);
495 window->grabFocusIfPointerInWindow();
496}
497
502
void cocoaRegisterTerminateHandler()
AppKit shuts us down via exit(), the code in main to cleanup is not run in that scenario.
#define i(x)
virtual Cursor getCursor() const
virtual void setCursor(Cursor aCursor)=0
static FGMouseCursor * instance()
osgViewer::View * getView()
Definition renderer.cxx:807
flightgear::FGEventHandler * getEventHandler()
Definition renderer.cxx:838
osgViewer::ViewerBase * getViewerBase() const
Definition renderer.cxx:782
static void resetPagerSingleton()
Definition scenery.cxx:567
static flightgear::SceneryPager * getPagerSingleton()
Definition scenery.cxx:560
void valueChanged(SGPropertyNode *node)
static CameraGroup * getDefault()
Get the default CameraGroup.
static void buildDefaultGroup(osgViewer::View *view)
Set the default CameraGroup, which is the only one that matters at this time.
static void setDefault(CameraGroup *group)
void setResizable(bool _resizable)
static void initWindowBuilder()
Initialize the singleton window builder.
Adapter from windows system / graphics context management API to functions used by flightgear.
static void setWSA(WindowSystemAdapter *wsa)
Set the global adapter.
WindowVector windows
Vector of all the registered windows.
static WindowSystemAdapter * getWSA()
Get the global WindowSystemAdapter.
SGCommandMgr::command_t command
void(* fgIdleHandler)()
Definition fg_os.hxx:68
void fgOSExit(int code)
void fgOSOpenWindow()
void updateOSGNotifyLevel()
void fgSetMouseCursor(FGMouseCursor::Cursor cursor)
SGPropertyNode * simTotalHostTime
int getNumDatabaseThreads()
void fgOSResetProperties()
static void ShowAffinities()
SGPropertyNode * frameWait
SGPropertyNode * simHost
SGPropertyNode * simFrameCount
static int status
FGMouseCursor::Cursor fgGetMouseCursor()
SGPropertyNode * simFrameResetCount
void fgWarpMouse(int x, int y)
int fgGetKeyModifiers()
void setNumDatabaseThreads(int threads)
void fgOSFullScreen()
void fgOSInit(int *argc, char **argv)
int fgOSMainLoop()
SGTimeStamp _lastUpdate
void fgOSCloseWindow()
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
Definition fg_props.cxx:556
bool fgSetInt(const char *name, int val)
Set an int value for a property.
Definition fg_props.cxx:568
void fgTie(const char *name, V(*getter)(), void(*setter)(V)=0, bool useDefault=true)
Tie a property to a pair of simple functions.
Definition fg_props.hxx:751
FGGlobals * globals
Definition globals.cxx:142
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
void addSentryBreadcrumb(const std::string &, const std::string &)
osg::Camera * getGUICamera(CameraGroup *cgroup)
Get the osg::Camera that draws the GUI, if any, from a camera group.
void warpGUIPointer(CameraGroup *cgroup, int x, int y)
Warp the pointer to coordinates in the GUI camera of a camera group.
Definition AIBase.hxx:25
static int atoi(const string &str)
Definition options.cxx:113
bool fgSetDouble(const char *name, double defaultValue)
Set a double value for a property.
Definition proptest.cpp:31
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
Definition proptest.cpp:25
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
Definition proptest.cpp:24
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
std::ostream & operator<<(std::ostream &out, const FGFrameInfo &frame_info)
void valueChanged(SGPropertyNode *node) override
SGPropertyNode_ptr m_node