From 3a79b71e80242c23c51a1e97c7cb966ee4eb0397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Tue, 23 Apr 2019 03:35:10 +0200 Subject: [PATCH 01/39] Compositor: Effects used by a compositor now receive a proper SGReaderWriterOptions and other misc fixes --- simgear/scene/viewer/ClusteredForward.cxx | 10 +- simgear/scene/viewer/ClusteredForward.hxx | 2 +- simgear/scene/viewer/Compositor.cxx | 12 +- simgear/scene/viewer/Compositor.hxx | 14 +- simgear/scene/viewer/CompositorBuffer.cxx | 5 +- simgear/scene/viewer/CompositorBuffer.hxx | 6 +- simgear/scene/viewer/CompositorPass.cxx | 232 +++++++++++++--------- simgear/scene/viewer/CompositorPass.hxx | 14 +- 8 files changed, 176 insertions(+), 119 deletions(-) diff --git a/simgear/scene/viewer/ClusteredForward.cxx b/simgear/scene/viewer/ClusteredForward.cxx index 6e5f4098..e2c9d593 100644 --- a/simgear/scene/viewer/ClusteredForward.cxx +++ b/simgear/scene/viewer/ClusteredForward.cxx @@ -49,9 +49,9 @@ Light LIGHT_LIST[NUM_LIGHTS] = { }; ///// END DEBUG -ClusteredForwardDrawCallback::ClusteredForwardDrawCallback() : +ClusteredForwardDrawCallback::ClusteredForwardDrawCallback(int tile_size) : _initialized(false), - _tile_size(64), + _tile_size(tile_size), _light_grid(new osg::Image), _light_indices(new osg::Image), _light_data(new osg::FloatArray(MAX_POINT_LIGHTS)) @@ -105,15 +105,15 @@ ClusteredForwardDrawCallback::operator()(osg::RenderInfo &renderInfo) const _light_data->setBufferObject(light_data_ubo.get()); #if OSG_VERSION_LESS_THAN(3,6,0) - osg::ref_ptr light_data_ubb = + osg::ref_ptr light_data_ubb = new osg::UniformBufferBinding(0, light_data_ubo.get(), 0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat)); #else - osg::ref_ptr light_data_ubb = + osg::ref_ptr light_data_ubb = new osg::UniformBufferBinding(0, _light_data.get(), 0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat)); #endif -light_data_ubb->setDataVariance(osg::Object::DYNAMIC); + light_data_ubb->setDataVariance(osg::Object::DYNAMIC); camera->getOrCreateStateSet()->setAttribute( light_data_ubb.get(), osg::StateAttribute::ON); diff --git a/simgear/scene/viewer/ClusteredForward.hxx b/simgear/scene/viewer/ClusteredForward.hxx index 8afa47b0..63da3af6 100644 --- a/simgear/scene/viewer/ClusteredForward.hxx +++ b/simgear/scene/viewer/ClusteredForward.hxx @@ -24,7 +24,7 @@ namespace compositor { class ClusteredForwardDrawCallback : public osg::Camera::DrawCallback { public: - ClusteredForwardDrawCallback(); + ClusteredForwardDrawCallback(int tile_size); virtual void operator()(osg::RenderInfo &renderInfo) const; protected: mutable bool _initialized; diff --git a/simgear/scene/viewer/Compositor.cxx b/simgear/scene/viewer/Compositor.cxx index f1caf50c..a86385b6 100644 --- a/simgear/scene/viewer/Compositor.cxx +++ b/simgear/scene/viewer/Compositor.cxx @@ -39,7 +39,8 @@ Compositor * Compositor::create(osg::View *view, osg::GraphicsContext *gc, osg::Viewport *viewport, - const SGPropertyNode *property_list) + const SGPropertyNode *property_list, + const SGReaderWriterOptions *options) { osg::ref_ptr compositor = new Compositor(view, gc, viewport); compositor->_name = property_list->getStringValue("name"); @@ -55,7 +56,7 @@ Compositor::create(osg::View *view, "a name to be available to passes. Skipping..."); continue; } - Buffer *buffer = buildBuffer(compositor.get(), p_buffer); + Buffer *buffer = buildBuffer(compositor.get(), p_buffer, options); if (buffer) compositor->addBuffer(buffer_name, buffer); } @@ -64,7 +65,7 @@ Compositor::create(osg::View *view, for (auto const &p_pass : p_passes) { if (!checkConditional(p_pass)) continue; - Pass *pass = buildPass(compositor.get(), p_pass); + Pass *pass = buildPass(compositor.get(), p_pass, options); if (pass) compositor->addPass(pass); } @@ -76,7 +77,8 @@ Compositor * Compositor::create(osg::View *view, osg::GraphicsContext *gc, osg::Viewport *viewport, - const std::string &name) + const std::string &name, + const SGReaderWriterOptions *options) { std::string filename(name); filename += ".xml"; @@ -96,7 +98,7 @@ Compositor::create(osg::View *view, return 0; } - return create(view, gc, viewport, property_list); + return create(view, gc, viewport, property_list, options); } Compositor::Compositor(osg::View *view, diff --git a/simgear/scene/viewer/Compositor.hxx b/simgear/scene/viewer/Compositor.hxx index c1761ef1..63be4e8b 100644 --- a/simgear/scene/viewer/Compositor.hxx +++ b/simgear/scene/viewer/Compositor.hxx @@ -73,7 +73,8 @@ public: static Compositor *create(osg::View *view, osg::GraphicsContext *gc, osg::Viewport *viewport, - const SGPropertyNode *property_list); + const SGPropertyNode *property_list, + const SGReaderWriterOptions *options); /** * \overload * \brief Create a Compositor from a file. @@ -84,7 +85,8 @@ public: static Compositor *create(osg::View *view, osg::GraphicsContext *gc, osg::Viewport *viewport, - const std::string &name); + const std::string &name, + const SGReaderWriterOptions *options); void update(const osg::Matrix &view_matrix, const osg::Matrix &proj_matrix); @@ -95,9 +97,11 @@ public: const osg::Vec2d& windowPos, osgUtil::LineSegmentIntersector::Intersections& intersections); - const osg::GraphicsContext *getGraphicsContext() const { return _gc; } + osg::View *getView() const { return _view; } - const osg::Viewport *getViewport() const { return _viewport; } + osg::GraphicsContext *getGraphicsContext() const { return _gc; } + + osg::Viewport *getViewport() const { return _viewport; } typedef std::array< osg::ref_ptr, @@ -121,8 +125,6 @@ public: Pass * getPass(const std::string &name) const; protected: - friend class PassBuilder; - osg::View *_view; osg::GraphicsContext *_gc; osg::ref_ptr _viewport; diff --git a/simgear/scene/viewer/CompositorBuffer.cxx b/simgear/scene/viewer/CompositorBuffer.cxx index 2acb6be9..80071ef4 100644 --- a/simgear/scene/viewer/CompositorBuffer.cxx +++ b/simgear/scene/viewer/CompositorBuffer.cxx @@ -28,6 +28,7 @@ #include #include #include +#include #include "Compositor.hxx" #include "CompositorUtil.hxx" @@ -49,6 +50,7 @@ PropStringMap buffer_format_map { {"rgba16f", {GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT}}, {"rgba32f", {GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT}}, {"r32f", {GL_R32F, GL_RED, GL_FLOAT}}, + {"rg16f", {GL_RG16F, GL_RG, GL_FLOAT}}, {"rg32f", {GL_RG32F, GL_RG, GL_FLOAT}}, {"depth16", {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}}, {"depth24", {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_FLOAT}}, @@ -92,7 +94,8 @@ PropStringMap shadow_compare_func_map = { }; Buffer * -buildBuffer(Compositor *compositor, const SGPropertyNode *node) +buildBuffer(Compositor *compositor, const SGPropertyNode *node, + const SGReaderWriterOptions *options) { std::string type = node->getStringValue("type"); if (type.empty()) { diff --git a/simgear/scene/viewer/CompositorBuffer.hxx b/simgear/scene/viewer/CompositorBuffer.hxx index df0a92a4..92a9d5e0 100644 --- a/simgear/scene/viewer/CompositorBuffer.hxx +++ b/simgear/scene/viewer/CompositorBuffer.hxx @@ -22,6 +22,9 @@ class SGPropertyNode; namespace simgear { + +class SGReaderWriterOptions; + namespace compositor { class Compositor; @@ -38,7 +41,8 @@ struct Buffer : public osg::Referenced { float width_scale, height_scale; }; -Buffer *buildBuffer(Compositor *compositor, const SGPropertyNode *node); +Buffer *buildBuffer(Compositor *compositor, const SGPropertyNode *node, + const SGReaderWriterOptions *options); } // namespace compositor } // namespace simgear diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index 8ee399d7..df28dfdf 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -22,9 +22,12 @@ #include #include +#include + #include #include #include +#include #include #include @@ -50,34 +53,40 @@ PropStringMap buffer_component_map = { {"packed-depth-stencil", osg::Camera::PACKED_DEPTH_STENCIL_BUFFER} }; -Pass * -PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) -{ - // The pass index matches its render order - int render_order = root->getIndex(); +Pass * +PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) +{ osg::ref_ptr pass = new Pass; + // The pass index matches its render order + pass->render_order = root->getIndex(); pass->name = root->getStringValue("name"); if (pass->name.empty()) { - SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Pass " << render_order + SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Pass " << pass->render_order << " has no name. It won't be addressable by name!"); } pass->type = root->getStringValue("type"); std::string eff_override_file = root->getStringValue("effect-override"); if (!eff_override_file.empty()) - pass->effect_override = makeEffect(eff_override_file, true, 0); + pass->effect_override = makeEffect(eff_override_file, true, options); osg::Camera *camera = new Camera; pass->camera = camera; camera->setName(pass->name); - camera->setGraphicsContext(compositor->_gc); + camera->setGraphicsContext(compositor->getGraphicsContext()); // Even though this camera will be added as a slave to the view, it will // always be updated manually in Compositor::update() camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); // Same with the projection matrix camera->setProjectionResizePolicy(osg::Camera::FIXED); + // We only use POST_RENDER. Leave PRE_RENDER for Canvas and other RTT stuff + // that doesn't involve the rendering pipeline itself. NESTED_RENDER is also + // not a possibility since we don't want to share RenderStage with the View + // master camera. + camera->setRenderOrder(osg::Camera::POST_RENDER, pass->render_order * 10); camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); // XXX: Should we make this configurable? @@ -144,7 +153,7 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); } catch (sg_exception &e) { SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping binding " - << p_binding->getIndex() << " in pass " << render_order + << p_binding->getIndex() << " in pass " << pass->render_order << ": " << e.what()); } } @@ -153,22 +162,15 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) if (p_attachments.empty()) { // If there are no attachments, assume the pass is rendering // directly to the screen - - camera->setRenderOrder(osg::Camera::NESTED_RENDER, render_order * 10); - // OSG cameras use the framebuffer by default, but it is stated - // explicitly anyway camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER); - camera->setDrawBuffer(GL_BACK); camera->setReadBuffer(GL_BACK); // Use the physical viewport. We can't let the user choose the viewport // size because some parts of the window might not be ours. - camera->setViewport(compositor->_viewport); + camera->setViewport(compositor->getViewport()); } else { // This is a RTT camera - - camera->setRenderOrder(osg::Camera::PRE_RENDER, render_order * 10); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); bool viewport_absolute = false; @@ -252,13 +254,13 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) camera->setViewport( 0, 0, - buffer->width_scale * compositor->_viewport->width(), - buffer->height_scale * compositor->_viewport->height()); + buffer->width_scale * compositor->getViewport()->width(), + buffer->height_scale * compositor->getViewport()->height()); } } } catch (sg_exception &e) { SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping attachment " - << p_attachment->getIndex() << " in pass " << render_order + << p_attachment->getIndex() << " in pass " << pass->render_order << ": " << e.what()); } } @@ -271,8 +273,10 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) struct QuadPassBuilder : public PassBuilder { public: - virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) { - osg::ref_ptr pass = PassBuilder::build(compositor, root); + virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) { + osg::ref_ptr pass = PassBuilder::build(compositor, root, options); + pass->useMastersSceneData = false; osg::Camera *camera = pass->camera; camera->setAllowEventFocus(false); @@ -289,11 +293,29 @@ public: scale = p_geometry->getFloatValue("scale", scale); } - const std::string eff_file = root->getStringValue("effect"); - - osg::ref_ptr quad = createFullscreenQuad( - left, bottom, width, height, scale, eff_file); + osg::ref_ptr quad = new EffectGeode; camera->addChild(quad); + quad->setCullingActive(false); + + const std::string eff_file = root->getStringValue("effect"); + if (!eff_file.empty()) { + Effect *eff = makeEffect(eff_file, true, options); + if (eff) + quad->setEffect(eff); + } + + osg::ref_ptr geom = createFullscreenQuadGeom( + left, bottom, width, height, scale); + quad->addDrawable(geom); + + osg::ref_ptr quad_state = quad->getOrCreateStateSet(); + int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED; + quad_state->setAttribute(new osg::PolygonMode( + osg::PolygonMode::FRONT_AND_BACK, + osg::PolygonMode::FILL), + values); + quad_state->setMode(GL_LIGHTING, values); + quad_state->setMode(GL_DEPTH_TEST, values); osg::StateSet *ss = camera->getOrCreateStateSet(); for (const auto &uniform : compositor->getUniforms()) @@ -302,13 +324,12 @@ public: return pass.release(); } protected: - osg::Geode *createFullscreenQuad(float left, - float bottom, - float width, - float height, - float scale, - const std::string &eff_file) { - osg::Geometry *geom; + osg::Geometry *createFullscreenQuadGeom(float left, + float bottom, + float width, + float height, + float scale) { + osg::ref_ptr geom; // When the quad is fullscreen, it can be optimized by using a // a fullscreen triangle instead of a quad to avoid discarding pixels @@ -349,31 +370,52 @@ protected: osg::PrimitiveSet::TRIANGLES, 0, 3)); } - osg::ref_ptr quad = new EffectGeode; - if (!eff_file.empty()) { - Effect *eff = makeEffect(eff_file, true, 0); - if (eff) - quad->setEffect(eff); - } - quad->addDrawable(geom); - quad->setCullingActive(false); - - osg::ref_ptr quad_state = quad->getOrCreateStateSet(); - int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED; - quad_state->setAttribute(new osg::PolygonMode( - osg::PolygonMode::FRONT_AND_BACK, - osg::PolygonMode::FILL), - values); - quad_state->setMode(GL_LIGHTING, values); - quad_state->setMode(GL_DEPTH_TEST, values); - - return quad.release(); + return geom.release(); } }; RegisterPassBuilder registerQuadPass("quad"); //------------------------------------------------------------------------------ +class ShadowMapCullCallback : public osg::NodeCallback { +public: + ShadowMapCullCallback(const std::string &suffix) { + _light_matrix_uniform = new osg::Uniform( + osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix); + } + + virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) { + osg::Camera *camera = static_cast(node); + + traverse(node, nv); + + // The light matrix uniform is updated after the traverse in case the + // OSG near/far plane calculations were enabled + osg::Matrixf light_matrix = + // Include the real camera inverse view matrix because if the shader + // used world coordinates, there would be precision issues. + _real_inverse_view * + camera->getViewMatrix() * + camera->getProjectionMatrix() * + // Bias matrices + osg::Matrix::translate(1.0, 1.0, 1.0) * + osg::Matrix::scale(0.5, 0.5, 0.5); + _light_matrix_uniform->set(light_matrix); + } + + void setRealInverseViewMatrix(const osg::Matrix &matrix) { + _real_inverse_view = matrix; + } + + osg::Uniform *getLightMatrixUniform() const { + return _light_matrix_uniform.get(); + } + +protected: + osg::Matrix _real_inverse_view; + osg::ref_ptr _light_matrix_uniform; +}; + class LightFinder : public osg::NodeVisitor { public: LightFinder(const std::string &name) : @@ -406,15 +448,14 @@ protected: struct ShadowMapUpdateCallback : public Pass::PassUpdateCallback { public: - ShadowMapUpdateCallback(const std::string &light_name, + ShadowMapUpdateCallback(ShadowMapCullCallback *cull_callback, + const std::string &light_name, float near_m, float far_m, - const std::string &suffix, int sm_width, int sm_height) : + _cull_callback(cull_callback), _light_finder(new LightFinder(light_name)), _near_m(near_m), _far_m(far_m) { - _light_matrix_uniform = new osg::Uniform( - osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix); _half_sm_size = osg::Vec2d((double)sm_width, (double)sm_height) / 2.0; } virtual void updatePass(Pass &pass, @@ -443,6 +484,7 @@ public: // This is not a problem though (for now). osg::Matrix view_inverse = osg::Matrix::inverse(view_matrix); + _cull_callback->setRealInverseViewMatrix(view_inverse); // Calculate the light's point of view transformation matrices. // Taken from Project Rembrandt. @@ -489,37 +531,30 @@ public: osg::Matrixd round_matrix = osg::Matrixd::translate( rounding.x(), rounding.y(), 0.0); light_proj_matrix *= round_matrix; - - osg::Matrixf light_matrix = - // Include the real camera inverse view matrix because if the shader - // used world coordinates, there would be precision issues. - view_inverse * - camera->getViewMatrix() * - camera->getProjectionMatrix() * - // Bias matrices - osg::Matrix::translate(1.0, 1.0, 1.0) * - osg::Matrix::scale(0.5, 0.5, 0.5); - _light_matrix_uniform->set(light_matrix); } - osg::Uniform *getLightMatrixUniform() const { - return _light_matrix_uniform.get(); - } protected: + osg::observer_ptr _cull_callback; osg::ref_ptr _light_finder; float _near_m; float _far_m; - osg::ref_ptr _light_matrix_uniform; osg::Vec2d _half_sm_size; }; struct ShadowMapPassBuilder : public PassBuilder { - virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) { - osg::ref_ptr pass = PassBuilder::build(compositor, root); - pass->useMastersSceneData = true; + virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) { + osg::ref_ptr pass = PassBuilder::build(compositor, root, options); osg::Camera *camera = pass->camera; camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); + camera->setCullingMode(camera->getCullingMode() & + ~osg::CullSettings::SMALL_FEATURE_CULLING); + //camera->setComputeNearFarMode( + // osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); + + ShadowMapCullCallback *cull_callback = new ShadowMapCullCallback(pass->name); + camera->setCullCallback(cull_callback); std::string light_name = root->getStringValue("light-name"); float near_m = root->getFloatValue("near-m"); @@ -527,9 +562,9 @@ struct ShadowMapPassBuilder : public PassBuilder { int sm_width = camera->getViewport()->width(); int sm_height = camera->getViewport()->height(); pass->update_callback = new ShadowMapUpdateCallback( + cull_callback, light_name, near_m, far_m, - pass->name, sm_width, sm_height); return pass.release(); @@ -626,9 +661,9 @@ protected: struct ScenePassBuilder : public PassBuilder { public: - virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) { - osg::ref_ptr pass = PassBuilder::build(compositor, root); - pass->useMastersSceneData = true; + virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) { + osg::ref_ptr pass = PassBuilder::build(compositor, root, options); pass->inherit_cull_mask = true; osg::Camera *camera = pass->camera; @@ -636,7 +671,8 @@ public: const SGPropertyNode *clustered = root->getChild("clustered-forward"); if (clustered) { - camera->setInitialDrawCallback(new ClusteredForwardDrawCallback); + int tile_size = clustered->getIntValue("tile-size", 64); + camera->setInitialDrawCallback(new ClusteredForwardDrawCallback(tile_size)); } int cubemap_face = root->getIntValue("cubemap-face", -1); @@ -644,23 +680,26 @@ public: float zFar = root->getFloatValue("z-far", 0.0f); pass->update_callback = new SceneUpdateCallback(cubemap_face, zNear, zFar); - std::string shadow_pass_name = root->getStringValue("use-shadow-pass"); - if (!shadow_pass_name.empty()) { - Pass *shadow_pass = compositor->getPass(shadow_pass_name); - if (shadow_pass) { - ShadowMapUpdateCallback *updatecb = - dynamic_cast( - shadow_pass->update_callback.get()); - if (updatecb) { - camera->getOrCreateStateSet()->addUniform( - updatecb->getLightMatrixUniform()); + PropertyList p_shadow_passes = root->getChildren("use-shadow-pass"); + for (const auto &p_shadow_pass : p_shadow_passes) { + std::string shadow_pass_name = p_shadow_pass->getStringValue(); + if (!shadow_pass_name.empty()) { + Pass *shadow_pass = compositor->getPass(shadow_pass_name); + if (shadow_pass) { + ShadowMapCullCallback *cullcb = + dynamic_cast( + shadow_pass->camera->getCullCallback()); + if (cullcb) { + camera->getOrCreateStateSet()->addUniform( + cullcb->getLightMatrixUniform()); + } else { + SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '" + << shadow_pass_name << "is not a shadow pass"); + } } else { - SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '" - << shadow_pass_name << "is not a shadow pass"); + SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not " + "find shadow pass named '" << shadow_pass_name << "'"); } - } else { - SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not " - "find shadow pass named '" << shadow_pass_name << "'"); } } @@ -673,7 +712,8 @@ RegisterPassBuilder registerScenePass("scene"); //------------------------------------------------------------------------------ Pass * -buildPass(Compositor *compositor, const SGPropertyNode *root) +buildPass(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) { std::string type = root->getStringValue("type"); if (type.empty()) { @@ -687,7 +727,7 @@ buildPass(Compositor *compositor, const SGPropertyNode *root) return 0; } - return builder->build(compositor, root); + return builder->build(compositor, root, options); } } // namespace compositor diff --git a/simgear/scene/viewer/CompositorPass.hxx b/simgear/scene/viewer/CompositorPass.hxx index 68fe89e3..09fe86f1 100644 --- a/simgear/scene/viewer/CompositorPass.hxx +++ b/simgear/scene/viewer/CompositorPass.hxx @@ -27,6 +27,9 @@ #include namespace simgear { + +class SGReaderWriterOptions; + namespace compositor { class Compositor; @@ -45,12 +48,13 @@ class Compositor; */ struct Pass : public osg::Referenced { Pass() : - useMastersSceneData(false), + useMastersSceneData(true), cull_mask(0xffffff), inherit_cull_mask(false), viewport_width_scale(0.0f), viewport_height_scale(0.0f) {} + int render_order; std::string name; std::string type; osg::ref_ptr camera; @@ -85,10 +89,11 @@ public: * and overrided for more special passes. * * @param compositor The Compositor instance that owns the pass. - * @param The root node of the pass property tree. + * @param root The root node of the pass property tree. * @return A Pass or a null pointer if an error occurred. */ - virtual Pass *build(Compositor *compositor, const SGPropertyNode *root); + virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options); static PassBuilder *find(const std::string &type) { auto itr = PassBuilderMapSingleton::instance()->_map.find(type); @@ -125,7 +130,8 @@ struct RegisterPassBuilder { * @param node The root node of the pass property tree. * @return A Pass or a null pointer if an error occurred. */ -Pass *buildPass(Compositor *compositor, const SGPropertyNode *root); +Pass *buildPass(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options); } // namespace compositor } // namespace simgear From 319b59500ce3ab2a528cd53fa13047cf7c915982 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Thu, 25 Apr 2019 19:19:19 +0200 Subject: [PATCH 02/39] Convert obsolete uses of get_filename_component( PATH) In the context of get_filename_component(), PATH is a legacy alias for DIRECTORY. Replace it with DIRECTORY, which is recommended[1] for CMake versions > 2.8.11. [1] https://cmake.org/cmake/help/latest/command/get_filename_component.html --- CMakeLists.txt | 4 ++-- CMakeModules/BoostTestTargets.cmake | 2 +- CMakeModules/CopyResourcesToBuildTree.cmake | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a2f59f1..8a48cb4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,7 +150,7 @@ endif() if (MSVC) - GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH) + GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} DIRECTORY) if (CMAKE_CL_64) SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64") else (CMAKE_CL_64) @@ -198,7 +198,7 @@ if (MSVC AND MSVC_3RDPARTY_ROOT) # if this variable was not set by the user, set it to 3rdparty root's # parent dir, which is the normal location for people using our # windows-3rd-party repo - GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH) + get_filename_component(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} DIRECTORY) set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR}) message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}") endif() diff --git a/CMakeModules/BoostTestTargets.cmake b/CMakeModules/BoostTestTargets.cmake index 3da31265..580bf9d2 100644 --- a/CMakeModules/BoostTestTargets.cmake +++ b/CMakeModules/BoostTestTargets.cmake @@ -80,7 +80,7 @@ if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000") set(_boostConfig "BoostTestTargetsDynamic.h") endif() endif() - get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH) + get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY) configure_file("${_moddir}/${_boostConfig}" "${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h" COPYONLY) diff --git a/CMakeModules/CopyResourcesToBuildTree.cmake b/CMakeModules/CopyResourcesToBuildTree.cmake index 3512cc48..0593363d 100644 --- a/CMakeModules/CopyResourcesToBuildTree.cmake +++ b/CMakeModules/CopyResourcesToBuildTree.cmake @@ -30,12 +30,12 @@ function(copy_resources_to_build_tree _target) endif() get_target_property(_path ${_target} LOCATION) - get_filename_component(_path "${_path}" PATH) + get_filename_component(_path "${_path}" DIRECTORY) if(NOT MSVC AND NOT "${CMAKE_GENERATOR}" MATCHES "Makefiles") foreach(_config ${CMAKE_CONFIGURATION_TYPES}) get_target_property(_path${_config} ${_target} LOCATION_${_config}) - get_filename_component(_path${_config} "${_path${_config}}" PATH) + get_filename_component(_path${_config} "${_path${_config}}" DIRECTORY) add_custom_command(TARGET ${_target} POST_BUILD COMMAND From dd38e399ca2a6d60e5b6280b5f0f3ac4a2ab7238 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Sat, 27 Apr 2019 11:23:14 +0200 Subject: [PATCH 03/39] Add readwav.hxx to HEADERS and readwav.cxx to SOURCES in all cases Previously, these two files were only added when SG was built with -DUSE_AEONWAVE:BOOL=OFF, because the SG code in use when USE_AEONWAVE is set to ON doesn't need readwav.hxx nor readwav.cxx. However, readwav.hxx and readwav.cxx are not only used in SG, but also in FG (at least by flightgear/src/Sound/VoiceSynthesizer.cxx), and this was causing a build failure when SG's USE_AEONWAVE and FG's ENABLE_FLITE flags were both set to ON: https://forum.flightgear.org/viewtopic.php?f=45&t=35672#p346633 and https://sourceforge.net/p/flightgear/mailman/message/36645567/ This commit should fix this build failure. --- simgear/sound/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/simgear/sound/CMakeLists.txt b/simgear/sound/CMakeLists.txt index b284d736..884e9520 100644 --- a/simgear/sound/CMakeLists.txt +++ b/simgear/sound/CMakeLists.txt @@ -6,6 +6,7 @@ set(HEADERS xmlsound.hxx soundmgr.hxx filters.hxx + readwav.hxx ) set(SOURCES @@ -13,6 +14,7 @@ set(SOURCES sample_group.cxx xmlsound.cxx filters.cxx + readwav.cxx ) if (USE_AEONWAVE) @@ -20,13 +22,9 @@ if (USE_AEONWAVE) soundmgr_aeonwave.cxx ) else() - set(HEADERS ${HEADERS} - readwav.hxx - ) set(SOURCES ${SOURCES} soundmgr_openal.cxx soundmgr_openal_private.hxx - readwav.cxx ) endif() From 3e57b5006664358c6f41757cf847901bdb0ec872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Sat, 4 May 2019 19:15:22 +0200 Subject: [PATCH 04/39] Added support for Effect schemes Effect schemes are a way of rendering an object in different ways depending on the Compositor pipeline. A new tag in the Compositor pipeline definition allows the user to choose which Effect scheme is going to be used for that pass. Every Effect will then be rendered using the technique that has a matching scheme name. If no valid technique is found, it won't be rendered. Since it would be a pain to define a valid technique for every scheme and for every Effect, the file '$FG_ROOT/Effects/schemes.xml' is introduced: test Effects/test If an Effect doesn't have a valid technique for the 'test' scheme, the 'Effects/test.eff' Effect will be merged with it. This process is done at initialization when techniques are being realized. --- simgear/scene/material/Effect.cxx | 55 +++++++++++++++++++- simgear/scene/material/Effect.hxx | 2 +- simgear/scene/material/EffectCullVisitor.cxx | 20 +++---- simgear/scene/material/EffectCullVisitor.hxx | 4 +- simgear/scene/material/Technique.hxx | 3 ++ simgear/scene/viewer/Compositor.cxx | 2 +- simgear/scene/viewer/CompositorPass.cxx | 5 +- simgear/scene/viewer/CompositorPass.hxx | 3 +- 8 files changed, 69 insertions(+), 25 deletions(-) diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx index 72f5fba1..994adeab 100644 --- a/simgear/scene/material/Effect.cxx +++ b/simgear/scene/material/Effect.cxx @@ -77,6 +77,7 @@ #include #include #include +#include #include #include #include @@ -245,11 +246,12 @@ int Effect::getGenerator(Effect::Generator what) const // There should always be a valid technique in an effect. -Technique* Effect::chooseTechnique(RenderInfo* info) +Technique* Effect::chooseTechnique(RenderInfo* info, const std::string &scheme) { BOOST_FOREACH(ref_ptr& technique, techniques) { - if (technique->valid(info) == Technique::VALID) + if (technique->valid(info) == Technique::VALID && + technique->getScheme() == scheme) return technique.get(); } return 0; @@ -1308,6 +1310,7 @@ void buildTechnique(Effect* effect, const SGPropertyNode* prop, { Technique* tniq = new Technique; effect->techniques.push_back(tniq); + tniq->setScheme(prop->getStringValue("scheme")); const SGPropertyNode* predProp = prop->getChild("predicate"); if (!predProp) { tniq->setAlwaysValid(true); @@ -1411,12 +1414,60 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss) return true; } +SGPropertyNode_ptr schemeList; + +void mergeSchemesFallbacks(Effect *effect, const SGReaderWriterOptions *options) +{ + if (!schemeList) { + schemeList = new SGPropertyNode; + const string schemes_file("Effects/schemes.xml"); + string absFileName + = SGModelLib::findDataFile(schemes_file, options); + if (absFileName.empty()) { + SG_LOG(SG_INPUT, SG_ALERT, "Could not find '" << schemes_file << "'"); + return; + } + try { + readProperties(absFileName, schemeList, 0, true); + } catch (sg_io_exception& e) { + SG_LOG(SG_INPUT, SG_ALERT, "Error reading '" << schemes_file << + "': " << e.getFormattedMessage()); + return; + } + } + + PropertyList p_schemes = schemeList->getChildren("scheme"); + for (const auto &p_scheme : p_schemes) { + string scheme_name = p_scheme->getStringValue("name"); + string fallback_name = p_scheme->getStringValue("fallback"); + if (scheme_name.empty() || fallback_name.empty()) + continue; + vector techniques = effect->root->getChildren("technique"); + auto it = std::find_if(techniques.begin(), techniques.end(), + [&scheme_name](const SGPropertyNode_ptr &tniq) { + return tniq->getStringValue("scheme") == scheme_name; + }); + // Only merge the fallback effect if we haven't found a technique + // implementing the scheme + if (it == techniques.end()) { + ref_ptr fallback = makeEffect(fallback_name, false, options); + if (fallback) { + SGPropertyNode *new_root = new SGPropertyNode; + mergePropertyTrees(new_root, effect->root, fallback->root); + effect->root = new_root; + effect->parametersProp = effect->root->getChild("parameters"); + } + } + } +} + // Walk the techniques property tree, building techniques and // passes. static SGMutex realizeTechniques_lock; bool Effect::realizeTechniques(const SGReaderWriterOptions* options) { SGGuard g(realizeTechniques_lock); + mergeSchemesFallbacks(this, options); if (_isRealized) return true; diff --git a/simgear/scene/material/Effect.hxx b/simgear/scene/material/Effect.hxx index 56a84769..496805a2 100644 --- a/simgear/scene/material/Effect.hxx +++ b/simgear/scene/material/Effect.hxx @@ -92,7 +92,7 @@ public: SGPropertyNode_ptr root; // Pointer to the parameters node, if it exists SGPropertyNode_ptr parametersProp; - Technique* chooseTechnique(osg::RenderInfo* renderInfo); + Technique* chooseTechnique(osg::RenderInfo* renderInfo, const std::string &scheme); virtual void resizeGLObjectBuffers(unsigned int maxSize); virtual void releaseGLObjects(osg::State* state = 0) const; /** diff --git a/simgear/scene/material/EffectCullVisitor.cxx b/simgear/scene/material/EffectCullVisitor.cxx index a016a455..36c31653 100644 --- a/simgear/scene/material/EffectCullVisitor.cxx +++ b/simgear/scene/material/EffectCullVisitor.cxx @@ -34,9 +34,9 @@ namespace simgear using osgUtil::CullVisitor; -EffectCullVisitor::EffectCullVisitor(bool collectLights, Effect *effectOverride) : +EffectCullVisitor::EffectCullVisitor(bool collectLights, const std::string &effScheme) : _collectLights(collectLights), - _effectOverride(effectOverride) + _effScheme(effScheme) { } @@ -62,18 +62,12 @@ void EffectCullVisitor::apply(osg::Geode& node) if (_collectLights && ( eg->getNodeMask() & MODELLIGHT_BIT ) ) { _lightList.push_back( eg ); } - Effect *effect; - if (_effectOverride) { - effect = _effectOverride; - } else { - effect = eg->getEffect(); - if (!effect) { - CullVisitor::apply(node); - return; - } - } + Effect* effect = eg->getEffect(); Technique* technique = 0; - if (!(technique = effect->chooseTechnique(&getRenderInfo()))) { + if (!effect) { + CullVisitor::apply(node); + return; + } else if (!(technique = effect->chooseTechnique(&getRenderInfo(), _effScheme))) { return; } // push the node's state. diff --git a/simgear/scene/material/EffectCullVisitor.hxx b/simgear/scene/material/EffectCullVisitor.hxx index ef93baa3..340ce7c7 100644 --- a/simgear/scene/material/EffectCullVisitor.hxx +++ b/simgear/scene/material/EffectCullVisitor.hxx @@ -34,7 +34,7 @@ class EffectGeode; class EffectCullVisitor : public osgUtil::CullVisitor { public: - EffectCullVisitor(bool collectLights = false, Effect *effectOverride = 0); + EffectCullVisitor(bool collectLights = false, const std::string &effScheme = ""); EffectCullVisitor(const EffectCullVisitor&); virtual osgUtil::CullVisitor* clone() const; using osgUtil::CullVisitor::apply; @@ -49,7 +49,7 @@ private: std::map > _bufferList; std::vector > _lightList; bool _collectLights; - osg::ref_ptr _effectOverride; + std::string _effScheme; }; } #endif diff --git a/simgear/scene/material/Technique.hxx b/simgear/scene/material/Technique.hxx index b2895cd1..6cca0a05 100644 --- a/simgear/scene/material/Technique.hxx +++ b/simgear/scene/material/Technique.hxx @@ -98,6 +98,8 @@ public: void setGLExtensionsPred(float glVersion, const std::vector& extensions); void refreshValidity(); + const std::string &getScheme() const { return _scheme; } + void setScheme(const std::string &scheme) { _scheme = scheme; } protected: // Validity of technique in a graphics context. struct ContextInfo : public osg::Referenced @@ -117,6 +119,7 @@ protected: osg::ref_ptr _shadowingStateSet; SGSharedPtr _validExpression; int _contextIdLocation; + std::string _scheme; }; class TechniquePredParser : public expression::ExpressionParser diff --git a/simgear/scene/viewer/Compositor.cxx b/simgear/scene/viewer/Compositor.cxx index a86385b6..8d3bd9b7 100644 --- a/simgear/scene/viewer/Compositor.cxx +++ b/simgear/scene/viewer/Compositor.cxx @@ -274,7 +274,7 @@ Compositor::addPass(Pass *pass) identifier = sceneView->getCullVisitor()->getIdentifier(); sceneView->setCullVisitor( - new EffectCullVisitor(false, pass->effect_override)); + new EffectCullVisitor(false, pass->effect_scheme)); sceneView->getCullVisitor()->setIdentifier(identifier.get()); identifier = sceneView->getCullVisitorLeft()->getIdentifier(); diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index df28dfdf..939cc7b6 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -67,10 +67,7 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, << " has no name. It won't be addressable by name!"); } pass->type = root->getStringValue("type"); - - std::string eff_override_file = root->getStringValue("effect-override"); - if (!eff_override_file.empty()) - pass->effect_override = makeEffect(eff_override_file, true, options); + pass->effect_scheme = root->getStringValue("effect-scheme"); osg::Camera *camera = new Camera; pass->camera = camera; diff --git a/simgear/scene/viewer/CompositorPass.hxx b/simgear/scene/viewer/CompositorPass.hxx index 09fe86f1..d8e26fa8 100644 --- a/simgear/scene/viewer/CompositorPass.hxx +++ b/simgear/scene/viewer/CompositorPass.hxx @@ -57,9 +57,8 @@ struct Pass : public osg::Referenced { int render_order; std::string name; std::string type; + std::string effect_scheme; osg::ref_ptr camera; - /** If null, there is no effect override for this pass. */ - osg::ref_ptr effect_override; bool useMastersSceneData; osg::Node::NodeMask cull_mask; /** Whether the cull mask is ANDed with the view master camera cull mask. */ From ae6ad2094080c3e5214b6f9b11a7563d52603cf5 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sat, 4 May 2019 15:26:25 -0500 Subject: [PATCH 05/39] [SGDemSession] fix uninitialized members. --- simgear/scene/dem/SGDemSession.cxx | 83 +++++++++++++----------- simgear/scene/dem/SGDemSession.hxx | 101 ++++++++++++++++++----------- 2 files changed, 108 insertions(+), 76 deletions(-) diff --git a/simgear/scene/dem/SGDemSession.cxx b/simgear/scene/dem/SGDemSession.cxx index fb95caec..391b8c6f 100644 --- a/simgear/scene/dem/SGDemSession.cxx +++ b/simgear/scene/dem/SGDemSession.cxx @@ -1,79 +1,86 @@ #include #include -SGDemSession::SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root ) +SGDemSession::SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root) { - setOffsets( SGDem::longitudeDegToOffset((double)mnLon), - SGDem::latitudeDegToOffset((double)mnLat), - SGDem::longitudeDegToOffset((double)mxLon), - SGDem::latitudeDegToOffset((double)mxLat) ); + setOffsets(SGDem::longitudeDegToOffset((double)mnLon), + SGDem::latitudeDegToOffset((double)mnLat), + SGDem::longitudeDegToOffset((double)mxLon), + SGDem::latitudeDegToOffset((double)mxLat)); - pDemRoot = root; - lvlIndex = idx; - lvlWidth = lvlW; + pDemRoot = root; + lvlIndex = idx; + lvlWidth = lvlW; lvlHeight = lvlH; } -SGDemSession::SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root ) { - setOffsets( SGDem::longitudeDegToOffset((double)mnLon), - SGDem::latitudeDegToOffset((double)mnLat), - SGDem::longitudeDegToOffset((double)mxLon), - SGDem::latitudeDegToOffset((double)mxLat) ); +SGDemSession::SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root) +{ + setOffsets(SGDem::longitudeDegToOffset((double)mnLon), + SGDem::latitudeDegToOffset((double)mnLat), + SGDem::longitudeDegToOffset((double)mxLon), + SGDem::latitudeDegToOffset((double)mxLat)); - pDemRoot = root; - lvlIndex = -1; // no level - session is raw input dir + pDemRoot = root; + lvlIndex = -1; // no level - session is raw input dir } -void SGDemSession::close( void ) +void SGDemSession::close(void) { - if ( tileRefs.size() ) { + if (tileRefs.size()) { tileRefs.clear(); - if ( lvlIndex >= 0 ) { - pDemRoot->flushCaches( lvlIndex ); + if (lvlIndex >= 0) { + pDemRoot->flushCaches(lvlIndex); } } } -void SGDemSession::getGeods( unsigned wo, unsigned so, unsigned eo, unsigned no, int resx, int resy, int incx, int incy, ::std::vector& geods, bool Debug1, bool Debug2 ) +void SGDemSession::getGeods(unsigned wo, unsigned so, unsigned eo, unsigned no, int resx, int resy, int incx, int incy, ::std::vector& geods, bool Debug1, bool Debug2) { // todo - store this info in deminfo unsigned span; // smallest tile width/height in level ( in offsets ) - switch( lvlIndex ) { - case 0: span = 1; break; // 1/8 deg - case 1: span = 16; break; // 2 degrees - case 2: span = 480; break; // 60 degrees - default: - fprintf( stderr, "invalid lvlIndex %d\n", lvlIndex ); - exit(0); + switch (lvlIndex) { + case 0: + span = 1; + break; // 1/8 deg + case 1: + span = 16; + break; // 2 degrees + case 2: + span = 480; + break; // 60 degrees + default: + fprintf(stderr, "invalid lvlIndex %d\n", lvlIndex); + exit(0); } - if ( lvlIndex >= 0 ) { + if (lvlIndex >= 0) { unsigned tileLon, tileLat; unsigned meshLon, meshLat; - int subx, suby; + int subx, suby; meshLon = wo; - tileLon = SGDem::roundDown( meshLon, lvlWidth); + tileLon = SGDem::roundDown(meshLon, lvlWidth); subx = (meshLon - tileLon) / span; // fprintf(stderr, "getGeods: lon is %lf : meshLon is %u, tileLon is %u, subx is %d\n", SGDem::offsetToLongitudeDeg(wo), meshLon, tileLon, subx ); meshLat = so; - tileLat = SGDem::roundDown( meshLat, lvlHeight ); + tileLat = SGDem::roundDown(meshLat, lvlHeight); suby = (meshLat - tileLat) / span; // fprintf(stderr, "getGeods: lat is %lf : meshLat is %u, tileLat is %u, suby is %d\n", SGDem::offsetToLatitudeDeg(so), meshLat, tileLat, suby ); // get the tle from the tile cache unsigned long key = tileLon << 16 | tileLat; - SGDemTileRef tile = pDemRoot->getTile( lvlIndex, key ); - if ( tile ) { + SGDemTileRef tile = pDemRoot->getTile(lvlIndex, key); + if (tile) { tile->getGeods(wo, so, eo, no, resx, resy, subx, suby, incx, incy, geods, Debug1, Debug2); } else { - fprintf(stderr, " *** ERROR: tile %d,%d not in session @ (%lf,%lf) - (%lf,%lf)\n", + fprintf(stderr, " *** ERROR: tile %u,%u not in session @ (%lf,%lf) - (%lf,%lf)\n", tileLon, tileLat, - SGDem::offsetToLongitudeDeg( west_off ), - SGDem::offsetToLatitudeDeg( south_off ), - SGDem::offsetToLongitudeDeg( east_off ), - SGDem::offsetToLatitudeDeg( north_off ) ); + SGDem::offsetToLongitudeDeg(west_off), + SGDem::offsetToLatitudeDeg(south_off), + SGDem::offsetToLongitudeDeg(east_off), + SGDem::offsetToLatitudeDeg(north_off)); } } } diff --git a/simgear/scene/dem/SGDemSession.hxx b/simgear/scene/dem/SGDemSession.hxx index f0813556..3d5cddc6 100644 --- a/simgear/scene/dem/SGDemSession.hxx +++ b/simgear/scene/dem/SGDemSession.hxx @@ -3,58 +3,83 @@ #include -class SGDemSession -{ +class SGDemSession { public: - SGDemSession() { - lvlIndex = -1; + SGDemSession() + : west_off(0) + , south_off(0) + , east_off(0) + , north_off(0) + , maxLon(0) + , maxLat(0) + , pDemRoot(NULL) + , lvlIndex(-1) + , lvlWidth(0) + , lvlHeight(0) + { } - SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root ); - SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root ); + SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root); + SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root); - SGDemSession( unsigned wo, unsigned so, unsigned eo, unsigned no, int idx, unsigned lvlW, unsigned lvlH, SGDemRoot* root ) { - setOffsets( wo, so, eo, no ); - - pDemRoot = root; - lvlIndex = idx; - lvlWidth = lvlW; - lvlHeight = lvlH; + SGDemSession(unsigned wo, unsigned so, unsigned eo, unsigned no, int idx, unsigned lvlW, unsigned lvlH, SGDemRoot* root) + : west_off(wo) + , south_off(so) + , east_off(eo) + , north_off(no) + , maxLon(0) + , maxLat(0) + , pDemRoot(root) + , lvlIndex(idx) + , lvlWidth(lvlW) + , lvlHeight(lvlH) + { } - SGDemSession( unsigned wo, unsigned so, unsigned eo, unsigned no, SGDemRoot* root ) { - setOffsets( wo, so, eo, no ); - - pDemRoot = root; - lvlIndex = -1; // no level - session is raw input dir + SGDemSession(unsigned wo, unsigned so, unsigned eo, unsigned no, SGDemRoot* root) + : west_off(wo) + , south_off(so) + , east_off(eo) + , north_off(no) + , maxLon(0) + , maxLat(0) + , pDemRoot(root) + , lvlIndex(-1) // no level - session is raw input dir + , lvlWidth(0) + , lvlHeight(0) + { } - ~SGDemSession() { + ~SGDemSession() + { close(); } - void addTile(SGDemTileRef pTile) { - tileRefs.push_back( pTile ); + void addTile(SGDemTileRef pTile) + { + tileRefs.push_back(pTile); } - const std::vector& getTiles( void ) const { + const std::vector& getTiles(void) const + { return tileRefs; } - unsigned int size( void ) const { + unsigned int size(void) const + { return tileRefs.size(); } - void getGeods( unsigned wp, unsigned so, unsigned eo, unsigned no, - int resx, int resy, int incx, int incy, - ::std::vector& geods, - bool Debug1, bool Debug2 - ); + void getGeods(unsigned wp, unsigned so, unsigned eo, unsigned no, + int resx, int resy, int incx, int incy, + ::std::vector& geods, + bool Debug1, bool Debug2); - void close( void ); + void close(void); - int getLvlIndex( void ) const { - return lvlIndex; + int getLvlIndex(void) const + { + return lvlIndex; }; private: @@ -64,13 +89,13 @@ private: east_off = eo; north_off = no; } - - unsigned west_off, south_off; - unsigned east_off, north_off; - int maxLon, maxLat; - SGDemRoot* pDemRoot; - int lvlIndex; - unsigned lvlWidth, lvlHeight; + + unsigned west_off, south_off; + unsigned east_off, north_off; + int maxLon, maxLat; + SGDemRoot* pDemRoot; + int lvlIndex; + unsigned lvlWidth, lvlHeight; std::vector tileRefs; }; From 1a429b63c5e8d62b9e174f5ba7c74534198f11a0 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sat, 4 May 2019 15:45:53 -0500 Subject: [PATCH 06/39] [SGImageUtils] eliminate unused variable. --- simgear/scene/util/SGImageUtils.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simgear/scene/util/SGImageUtils.cxx b/simgear/scene/util/SGImageUtils.cxx index 7f79cd7b..58aad3b2 100644 --- a/simgear/scene/util/SGImageUtils.cxx +++ b/simgear/scene/util/SGImageUtils.cxx @@ -1812,7 +1812,7 @@ namespace { static void write(const ImageUtils::PixelWriter* iw, const osg::Vec4f& c, int s, int t, int r, int m) { - GLubyte* ptr = (GLubyte*)iw->data(s, t, r, m); + iw->data(s, t, r, m); //OE_WARN << LC << "Target GL_UNSIGNED_BYTE_3_3_2 not yet implemented" << std::endl; } }; From b3ef2478f5a3d409cd6a996c1e50f8aa35a33233 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Tue, 7 May 2019 05:13:55 +0200 Subject: [PATCH 07/39] Instrumented Nasal GC --- simgear/nasal/code.c | 26 +++++++++++++++++++++++--- simgear/nasal/gc.c | 24 ++++++++++++++++++------ simgear/nasal/misc.c | 1 + simgear/timing/timestamp.cxx | 18 ++++++++++++++++++ simgear/timing/timestamp.hxx | 8 ++++++-- 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index faf534ec..740b7058 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -24,6 +24,21 @@ struct Globals* globals = 0; static naRef bindFunction(naContext ctx, struct Frame* f, naRef code); +char __name[3000] = { 0 }; +int init = 0; +void getSource(struct Context* c) { + naRef v = naGetSourceFile(c, 0); + init = 1; + if (!IS_NIL(v)) + snprintf(__name, 3000, "%s:%d", naStr_data(v), naGetLine(c, 0)); + else + *__name = 0; +} +char *getName() { + if (init) + return __name; + return "**"; +} #define ERR(c, msg) naRuntimeError((c),(msg)) void naRuntimeError(naContext c, const char* fmt, ...) { @@ -305,6 +320,7 @@ static void checkNamedArgs(naContext ctx, struct naCode* c, struct naHash* h) static struct Frame* setupFuncall(naContext ctx, int nargs, int mcall, int named) { + getSource(ctx); naRef *args, func, code, obj = naNil(); struct Frame* f; int opf = ctx->opTop - nargs; @@ -833,9 +849,13 @@ naRef naGetSourceFile(naContext ctx, int frame) { naRef f; frame = findFrame(ctx, &ctx, frame); - f = ctx->fStack[frame].func; - f = PTR(f).func->code; - return PTR(f).code->srcFile; + if (frame >= 0) { + f = ctx->fStack[frame].func; + f = PTR(f).func->code; + if (!IS_NIL(f) && PTR(f).code) + return PTR(f).code->srcFile; + } + return naNil(); } char* naGetError(naContext ctx) diff --git a/simgear/nasal/gc.c b/simgear/nasal/gc.c index 5ac9c43c..7c0a9183 100644 --- a/simgear/nasal/gc.c +++ b/simgear/nasal/gc.c @@ -1,7 +1,6 @@ #include "nasal.h" #include "data.h" #include "code.h" - #define MIN_BLOCK_SIZE 32 static void reap(struct naPool* p); @@ -12,14 +11,17 @@ struct Block { char* block; struct Block* next; }; - // Must be called with the giant exclusive lock! +extern void global_stamp(); +extern int global_elapsedUSec(); +extern char *getName(); static void freeDead() { int i; for(i=0; indead; i++) naFree(globals->deadBlocks[i]); globals->ndead = 0; + printf("--> freedead (%d) : %d", i, global_elapsedUSec()); } static void marktemps(struct Context* c) @@ -52,6 +54,7 @@ static void garbageCollect() marktemps(c); c = c->nextAll; } + printf("--> garbageCollect: %d ", global_elapsedUSec()); mark(globals->save); mark(globals->save_hash); @@ -60,10 +63,13 @@ static void garbageCollect() mark(globals->argRef); mark(globals->parentsRef); - // Finally collect all the freed objects - for(i=0; ipools[i])); + printf("m> %d", global_elapsedUSec()); + // Finally collect all the freed objects + for (i = 0; i < NUM_NASAL_TYPES; i++) { + reap(&(globals->pools[i])); + printf(" p(%d)> %d", i, global_elapsedUSec()); + } // Make enough space for the dead blocks we need to free during // execution. This works out to 1 spot for every 2 live objects, // which should be limit the number of bottleneck operations @@ -75,6 +81,7 @@ static void garbageCollect() globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz); } globals->needGC = 0; + printf(">> %d ", global_elapsedUSec()); } void naModLock() @@ -104,6 +111,7 @@ void naModUnlock() // you think about it). static void bottleneck() { + global_stamp(); struct Globals* g = globals; g->bottleneck = 1; while(g->bottleneck && g->waitCount < g->nThreads - 1) { @@ -111,12 +119,16 @@ static void bottleneck() UNLOCK(); naSemDown(g->sem); LOCK(); g->waitCount--; } + printf("bottleneck wait finished %d usec", global_elapsedUSec()); if(g->waitCount >= g->nThreads - 1) { freeDead(); - if(g->needGC) garbageCollect(); + //if(g->needGC) + garbageCollect(); if(g->waitCount) naSemUp(g->sem, g->waitCount); g->bottleneck = 0; } + char *c = getName(); + printf("bottleneck finished: %d %s\n", global_elapsedUSec(), c); } void naGC() diff --git a/simgear/nasal/misc.c b/simgear/nasal/misc.c index 0a5c8615..764d113d 100644 --- a/simgear/nasal/misc.c +++ b/simgear/nasal/misc.c @@ -65,6 +65,7 @@ naRef naStringValue(naContext c, naRef r) naRef naNew(struct Context* c, int type) { + getSource(c); naRef result; if(c->nfree[type] == 0) c->free[type] = naGC_get(&globals->pools[type], diff --git a/simgear/timing/timestamp.cxx b/simgear/timing/timestamp.cxx index 067a1b95..d9a2ef04 100644 --- a/simgear/timing/timestamp.cxx +++ b/simgear/timing/timestamp.cxx @@ -337,3 +337,21 @@ int SGTimeStamp::elapsedMSec() const return static_cast((now - *this).toMSecs()); } + +int SGTimeStamp::elapsedUSec() const +{ + SGTimeStamp now; + now.stamp(); + + return static_cast((now - *this).toUSecs()); +} +extern "C" { + SGTimeStamp global_timestamp; + void global_stamp() { + global_timestamp.stamp(); + } + extern int global_elapsedUSec() + { + return global_timestamp.elapsedUSec(); + } + } \ No newline at end of file diff --git a/simgear/timing/timestamp.hxx b/simgear/timing/timestamp.hxx index 266fd2ab..bf1d5d40 100644 --- a/simgear/timing/timestamp.hxx +++ b/simgear/timing/timestamp.hxx @@ -221,9 +221,13 @@ public: { return sleepFor(fromMSec(msec)); } /** - * elapsed time since the stamp was taken, in msec - */ + * elapsed time since the stamp was taken, in msec + */ int elapsedMSec() const; + /** + * elapsed time since the stamp was taken, in usec + */ + int elapsedUSec() const; private: SGTimeStamp(sec_type sec, nsec_type nsec) { setTime(sec, nsec); } From c06eabff2430cd7201c7271a1d8a6c2168f02213 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Tue, 7 May 2019 05:13:55 +0200 Subject: [PATCH 08/39] Instrumented Nasal GC --- simgear/nasal/code.c | 26 +++++++++++++++++++++++--- simgear/nasal/gc.c | 24 ++++++++++++++++++------ simgear/nasal/misc.c | 1 + simgear/timing/timestamp.cxx | 18 ++++++++++++++++++ simgear/timing/timestamp.hxx | 8 ++++++-- 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index faf534ec..740b7058 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -24,6 +24,21 @@ struct Globals* globals = 0; static naRef bindFunction(naContext ctx, struct Frame* f, naRef code); +char __name[3000] = { 0 }; +int init = 0; +void getSource(struct Context* c) { + naRef v = naGetSourceFile(c, 0); + init = 1; + if (!IS_NIL(v)) + snprintf(__name, 3000, "%s:%d", naStr_data(v), naGetLine(c, 0)); + else + *__name = 0; +} +char *getName() { + if (init) + return __name; + return "**"; +} #define ERR(c, msg) naRuntimeError((c),(msg)) void naRuntimeError(naContext c, const char* fmt, ...) { @@ -305,6 +320,7 @@ static void checkNamedArgs(naContext ctx, struct naCode* c, struct naHash* h) static struct Frame* setupFuncall(naContext ctx, int nargs, int mcall, int named) { + getSource(ctx); naRef *args, func, code, obj = naNil(); struct Frame* f; int opf = ctx->opTop - nargs; @@ -833,9 +849,13 @@ naRef naGetSourceFile(naContext ctx, int frame) { naRef f; frame = findFrame(ctx, &ctx, frame); - f = ctx->fStack[frame].func; - f = PTR(f).func->code; - return PTR(f).code->srcFile; + if (frame >= 0) { + f = ctx->fStack[frame].func; + f = PTR(f).func->code; + if (!IS_NIL(f) && PTR(f).code) + return PTR(f).code->srcFile; + } + return naNil(); } char* naGetError(naContext ctx) diff --git a/simgear/nasal/gc.c b/simgear/nasal/gc.c index 5ac9c43c..7c0a9183 100644 --- a/simgear/nasal/gc.c +++ b/simgear/nasal/gc.c @@ -1,7 +1,6 @@ #include "nasal.h" #include "data.h" #include "code.h" - #define MIN_BLOCK_SIZE 32 static void reap(struct naPool* p); @@ -12,14 +11,17 @@ struct Block { char* block; struct Block* next; }; - // Must be called with the giant exclusive lock! +extern void global_stamp(); +extern int global_elapsedUSec(); +extern char *getName(); static void freeDead() { int i; for(i=0; indead; i++) naFree(globals->deadBlocks[i]); globals->ndead = 0; + printf("--> freedead (%d) : %d", i, global_elapsedUSec()); } static void marktemps(struct Context* c) @@ -52,6 +54,7 @@ static void garbageCollect() marktemps(c); c = c->nextAll; } + printf("--> garbageCollect: %d ", global_elapsedUSec()); mark(globals->save); mark(globals->save_hash); @@ -60,10 +63,13 @@ static void garbageCollect() mark(globals->argRef); mark(globals->parentsRef); - // Finally collect all the freed objects - for(i=0; ipools[i])); + printf("m> %d", global_elapsedUSec()); + // Finally collect all the freed objects + for (i = 0; i < NUM_NASAL_TYPES; i++) { + reap(&(globals->pools[i])); + printf(" p(%d)> %d", i, global_elapsedUSec()); + } // Make enough space for the dead blocks we need to free during // execution. This works out to 1 spot for every 2 live objects, // which should be limit the number of bottleneck operations @@ -75,6 +81,7 @@ static void garbageCollect() globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz); } globals->needGC = 0; + printf(">> %d ", global_elapsedUSec()); } void naModLock() @@ -104,6 +111,7 @@ void naModUnlock() // you think about it). static void bottleneck() { + global_stamp(); struct Globals* g = globals; g->bottleneck = 1; while(g->bottleneck && g->waitCount < g->nThreads - 1) { @@ -111,12 +119,16 @@ static void bottleneck() UNLOCK(); naSemDown(g->sem); LOCK(); g->waitCount--; } + printf("bottleneck wait finished %d usec", global_elapsedUSec()); if(g->waitCount >= g->nThreads - 1) { freeDead(); - if(g->needGC) garbageCollect(); + //if(g->needGC) + garbageCollect(); if(g->waitCount) naSemUp(g->sem, g->waitCount); g->bottleneck = 0; } + char *c = getName(); + printf("bottleneck finished: %d %s\n", global_elapsedUSec(), c); } void naGC() diff --git a/simgear/nasal/misc.c b/simgear/nasal/misc.c index 0a5c8615..764d113d 100644 --- a/simgear/nasal/misc.c +++ b/simgear/nasal/misc.c @@ -65,6 +65,7 @@ naRef naStringValue(naContext c, naRef r) naRef naNew(struct Context* c, int type) { + getSource(c); naRef result; if(c->nfree[type] == 0) c->free[type] = naGC_get(&globals->pools[type], diff --git a/simgear/timing/timestamp.cxx b/simgear/timing/timestamp.cxx index 067a1b95..d9a2ef04 100644 --- a/simgear/timing/timestamp.cxx +++ b/simgear/timing/timestamp.cxx @@ -337,3 +337,21 @@ int SGTimeStamp::elapsedMSec() const return static_cast((now - *this).toMSecs()); } + +int SGTimeStamp::elapsedUSec() const +{ + SGTimeStamp now; + now.stamp(); + + return static_cast((now - *this).toUSecs()); +} +extern "C" { + SGTimeStamp global_timestamp; + void global_stamp() { + global_timestamp.stamp(); + } + extern int global_elapsedUSec() + { + return global_timestamp.elapsedUSec(); + } + } \ No newline at end of file diff --git a/simgear/timing/timestamp.hxx b/simgear/timing/timestamp.hxx index 266fd2ab..bf1d5d40 100644 --- a/simgear/timing/timestamp.hxx +++ b/simgear/timing/timestamp.hxx @@ -221,9 +221,13 @@ public: { return sleepFor(fromMSec(msec)); } /** - * elapsed time since the stamp was taken, in msec - */ + * elapsed time since the stamp was taken, in msec + */ int elapsedMSec() const; + /** + * elapsed time since the stamp was taken, in usec + */ + int elapsedUSec() const; private: SGTimeStamp(sec_type sec, nsec_type nsec) { setTime(sec, nsec); } From 14971f88ee669070720caf34114d0cfdfd253c5c Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Thu, 9 May 2019 20:52:20 -0500 Subject: [PATCH 09/39] [soundmgr_openal] Pause/Resume Sound. The following changes fixes a case for me where I hear the sound change levels up and down for each pause un-pause cycle. Patch provided by daniel.c.wickstrom@gmail.com. --- simgear/sound/soundmgr_openal.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simgear/sound/soundmgr_openal.cxx b/simgear/sound/soundmgr_openal.cxx index cc3b420a..8b91652d 100644 --- a/simgear/sound/soundmgr_openal.cxx +++ b/simgear/sound/soundmgr_openal.cxx @@ -340,7 +340,7 @@ void SGSoundMgr::suspend() for ( auto current = d->_sample_groups.begin(); current != d->_sample_groups.end(); ++current ) { SGSampleGroup *sgrp = current->second; - sgrp->stop(); + sgrp->suspend(); } _active = false; } From 7354201b5ddd420a230bb4910d3c3d859a5a46cc Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Mon, 13 May 2019 15:21:08 +0200 Subject: [PATCH 10/39] Added background (threaded) garbage collector --- simgear/nasal/CMakeLists.txt | 1 + simgear/nasal/ThreadedGarbageCollector.cpp | 176 ++++++++++++ simgear/nasal/code.c | 46 +-- .../nasal/cppbind/detail/to_nasal_helper.cxx | 118 ++++++++ simgear/nasal/gc.c | 263 +++++++++++++++--- simgear/nasal/hash.c | 11 + simgear/nasal/misc.c | 2 +- 7 files changed, 554 insertions(+), 63 deletions(-) create mode 100644 simgear/nasal/ThreadedGarbageCollector.cpp diff --git a/simgear/nasal/CMakeLists.txt b/simgear/nasal/CMakeLists.txt index 8f4d2c90..e5881450 100644 --- a/simgear/nasal/CMakeLists.txt +++ b/simgear/nasal/CMakeLists.txt @@ -28,6 +28,7 @@ set(SOURCES code.h data.h parse.h + ThreadedGarbageCollector.cpp ) simgear_component(nasal nasal "${SOURCES}" "${HEADERS}") diff --git a/simgear/nasal/ThreadedGarbageCollector.cpp b/simgear/nasal/ThreadedGarbageCollector.cpp new file mode 100644 index 00000000..22133b0c --- /dev/null +++ b/simgear/nasal/ThreadedGarbageCollector.cpp @@ -0,0 +1,176 @@ +//#include "nasal.h" +//#include "data.h" +//#include "code.h" + +#include +#include +#include +#include +#include +#include +extern "C" { + extern int __bg_gc; + extern int GCglobalAlloc(); + extern int naGarbageCollect(); +} + +class SGExclusiveThread : public SGThread +{ +private: + std::mutex mutex_; + std::condition_variable condVar; + SGTimeStamp timestamp; + std::mutex Cmutex_; + std::condition_variable CcondVar; + + bool _started; + bool _terminated; + int last_await_time; + + std::atomic dataReady; + std::atomic complete; + std::atomic process_ran; + std::atomic process_running; + +public: + SGExclusiveThread() : + _started(false), _terminated(false), last_await_time(0), + dataReady(false), complete(true), process_ran(false), process_running(false) + { + } + + virtual ~SGExclusiveThread() + { + + } + + void release() { + std::unique_lock lck(mutex_); + if (!complete) { + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] not finished - skipping"); + return; + } + if (!complete.exchange(false)) + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] concurrent failure (2)"); + if (dataReady.exchange(true)) + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] concurrent failure (1)"); + condVar.notify_one(); + } + void wait() { + std::unique_lock lck(mutex_); + if (!dataReady) + { + do + { + condVar.wait(lck); + } while (!dataReady); + } + } + void clearAwaitCompletionTime() { + last_await_time = 0; + } + virtual void awaitCompletion() { + timestamp.stamp(); + std::unique_lock lck(Cmutex_); + if (!complete) + { + do { + CcondVar.wait(lck); + } while (!complete.load()); + } + + if (process_ran) { + last_await_time = timestamp.elapsedUSec(); + printf("await %5.1f ", last_await_time / 1000.0); + process_ran = 0; + } + } + + void setCompletion() { + std::unique_lock lck(Cmutex_); + if (!dataReady.exchange(false)) + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] atomic operation on dataReady failed (5)\n"); + + if (complete.exchange(true)) + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] atomic operation on complete failed (5)\n"); + CcondVar.notify_one(); + } + virtual int process() = 0; + virtual void run() + { + process_running = true; + while (!_terminated) { + wait(); + process_ran = process(); + setCompletion(); + } + process_running = false; + _terminated = false; + _started = false; + } + + void terminate() { + _terminated = true; + } + bool stop() + { + return true; + } + void ensure_running() + { + if (!_started) + { + _started = true; + start(); + } + } + bool is_running() + { + return process_running; + } + +}; + +class ThreadedGarbageCollector : public SGExclusiveThread +{ +public: + ThreadedGarbageCollector() : SGExclusiveThread() + { + } + virtual ~ThreadedGarbageCollector() + { + + } + + virtual int process() + { + return naGarbageCollect(); + } +}; + +ThreadedGarbageCollector gct; +extern"C" { + void startNasalBackgroundGarbageCollection() + { + gct.ensure_running(); + } + void stopNasalBackgroundGarbageCollection() + { + gct.terminate(); + } + void performNasalBackgroundGarbageCollection() + { + if (gct.is_running()) + gct.release(); + } + void awaitNasalGarbageCollectionComplete(bool can_wait) + { + if (gct.is_running()) + { + if (can_wait) + gct.awaitCompletion(); + else + gct.clearAwaitCompletionTime(); + } + } +} diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index 740b7058..b7d3674b 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -24,21 +24,21 @@ struct Globals* globals = 0; static naRef bindFunction(naContext ctx, struct Frame* f, naRef code); -char __name[3000] = { 0 }; -int init = 0; -void getSource(struct Context* c) { - naRef v = naGetSourceFile(c, 0); - init = 1; - if (!IS_NIL(v)) - snprintf(__name, 3000, "%s:%d", naStr_data(v), naGetLine(c, 0)); - else - *__name = 0; -} -char *getName() { - if (init) - return __name; - return "**"; -} +//char __name[3000] = { 0 }; +//int init = 0; +//void getSource(struct Context* c) { +// naRef v = naGetSourceFile(c, 0); +// init = 1; +// if (!IS_NIL(v)) +// snprintf(__name, 3000, "%s:%d", naStr_data(v), naGetLine(c, 0)); +// else +// *__name = 0; +//} +//char *getName() { +// if (init) +// return __name; +// return "**"; +//} #define ERR(c, msg) naRuntimeError((c),(msg)) void naRuntimeError(naContext c, const char* fmt, ...) { @@ -172,7 +172,7 @@ static void initContext(naContext c) c->error[0] = 0; c->userData = 0; } - +#define BASE_SIZE 256000 static void initGlobals() { int i; @@ -183,10 +183,10 @@ static void initGlobals() globals->sem = naNewSem(); globals->lock = naNewLock(); - globals->allocCount = 256; // reasonable starting value + globals->allocCount = BASE_SIZE; // reasonable starting value for(i=0; ipools[i]), i); - globals->deadsz = 256; + globals->deadsz = BASE_SIZE; globals->ndead = 0; globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz); @@ -320,7 +320,7 @@ static void checkNamedArgs(naContext ctx, struct naCode* c, struct naHash* h) static struct Frame* setupFuncall(naContext ctx, int nargs, int mcall, int named) { - getSource(ctx); + //getSource(ctx); naRef *args, func, code, obj = naNil(); struct Frame* f; int opf = ctx->opTop - nargs; @@ -351,8 +351,9 @@ static struct Frame* setupFuncall(naContext ctx, int nargs, int mcall, int named f->ip = 0; f->bp = ctx->opFrame; - if(mcall) naHash_set(f->locals, globals->meRef, obj); - + if (mcall) { + naHash_set(f->locals, globals->meRef, obj); + } if(named) checkNamedArgs(ctx, PTR(code).code, PTR(f->locals).hash); else setupArgs(ctx, f, args, nargs); @@ -921,8 +922,9 @@ naRef naCall(naContext ctx, naRef func, int argc, naRef* args, func = naNewFunc(ctx, func); PTR(func).func->namespace = locals; } - if(!IS_NIL(obj)) + if (!IS_NIL(obj)) { naHash_set(locals, globals->meRef, obj); + } ctx->opTop = ctx->markTop = 0; ctx->fTop = 1; diff --git a/simgear/nasal/cppbind/detail/to_nasal_helper.cxx b/simgear/nasal/cppbind/detail/to_nasal_helper.cxx index 752a7d5a..e2a04270 100644 --- a/simgear/nasal/cppbind/detail/to_nasal_helper.cxx +++ b/simgear/nasal/cppbind/detail/to_nasal_helper.cxx @@ -25,6 +25,12 @@ #include +#include +#include +#include +#include + + namespace nasal { //---------------------------------------------------------------------------- @@ -123,4 +129,116 @@ namespace nasal ); } + template class FastStack + { + public: + T* st; + int allocationSize; + int lastIndex; + //std::mutex mutex_; + + public: + FastStack(int stackSize); + ~FastStack(); + + inline void resize(int newSize); + inline void push(T x); + inline void pop(); + inline void clear(); + inline void iterate(int(*process)(naRef v)); + inline size_t size() { + return lastIndex + 1; + } + T top() + { + //std::unique_lock lck(mutex_); + return st[lastIndex]; + } + void push_if_not_present(naRef r); + }; + + template + FastStack::FastStack(int stackSize) + { + st = NULL; + this->allocationSize = stackSize; + st = (T*)malloc(stackSize * sizeof(naRef)); + lastIndex = -1; + } + template + FastStack::~FastStack() + { + delete[] st; + } + + template + void FastStack::clear() + { + lastIndex = -1; + } + + template + void FastStack::push_if_not_present(naRef r) { + /*for (int i = 0; i <= lastIndex; i++) + if (st[i] == r) + return;*/ + push(r); + } + template + void FastStack::iterate(int(*process)(naRef v)) + { + for (int i = 0; i <= lastIndex; i++) + if (process(st[i])) + break; + } + + template + void FastStack::pop() + { + --lastIndex; + } + + template + void FastStack::push(T x) + { + if (++lastIndex >= allocationSize) + resize(allocationSize * 2); + st[lastIndex] = x; + } + + template + void FastStack::resize(int newSize) + { + //std::unique_lock lck(mutex_); + T* new_st = (T*)realloc(st, newSize * sizeof(naRef)); + if (new_st) + { + st = new_st; + allocationSize = newSize; + SG_LOG(SG_NASAL, SG_WARN, "Increased tc stack to " << allocationSize); + } + else + throw "Failed to grow tc stack"; + } + FastStack < naRef> t_stack(40); + extern"C" { + + + int __stack_hwm = 0; + void na_t_stack_push(naRef v) { + t_stack.push(v); + + if (t_stack.size() > __stack_hwm) + __stack_hwm = t_stack.size(); + } + extern int na_t_stack_count() { + return t_stack.size(); + } + extern naRef na_t_stack_pop() + { + naRef v = t_stack.top(); + t_stack.pop(); + return v; + } + } } // namespace nasal diff --git a/simgear/nasal/gc.c b/simgear/nasal/gc.c index 7c0a9183..d62ceab4 100644 --- a/simgear/nasal/gc.c +++ b/simgear/nasal/gc.c @@ -5,6 +5,8 @@ static void reap(struct naPool* p); static void mark(naRef r); +static void process_all(naRef r, int(*process)(naRef r)); + struct Block { int size; @@ -14,14 +16,15 @@ struct Block { // Must be called with the giant exclusive lock! extern void global_stamp(); extern int global_elapsedUSec(); -extern char *getName(); -static void freeDead() +int nasal_gc_old = 0; + +static int freeDead() { int i; for(i=0; indead; i++) naFree(globals->deadBlocks[i]); globals->ndead = 0; - printf("--> freedead (%d) : %d", i, global_elapsedUSec()); + return i; } static void marktemps(struct Context* c) @@ -34,54 +37,105 @@ static void marktemps(struct Context* c) } } +int __elements_visited = 0; +extern int __stack_hwm; +int busy=0; // Must be called with the big lock! static void garbageCollect() { + if (busy) + return; + busy = 1; int i; struct Context* c; globals->allocCount = 0; c = globals->allContexts; - while(c) { - for(i=0; iallContexts; + while (c) { + ctxc++; + for (i = 0; i < NUM_NASAL_TYPES; i++) c->nfree[i] = 0; - for(i=0; i < c->fTop; i++) { + for (i = 0; i < c->fTop; i++) { mark(c->fStack[i].func); mark(c->fStack[i].locals); } - for(i=0; i < c->opTop; i++) + for (i = 0; i < c->opTop; i++) mark(c->opStack[i]); mark(c->dieArg); marktemps(c); c = c->nextAll; } - printf("--> garbageCollect: %d ", global_elapsedUSec()); + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + printf("--> garbageCollect(#e%-5d): %-4d ", eel, et); mark(globals->save); + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + printf("s(%5d) %-5d ", eel, et); + mark(globals->save_hash); + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + printf("h(%5d) %-5d ", eel, et); + + mark(globals->symbols); + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + //printf("sy(%5d) %-4d ", eel, et); + mark(globals->meRef); + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + //printf("me(%5d) %-5d ", eel, et); + mark(globals->argRef); + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + //printf("ar(%5d) %-5d ", eel, et); + mark(globals->parentsRef); - - printf("m> %d", global_elapsedUSec()); - + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + //printf(" ev[%3d] %-5d", eel, et); // Finally collect all the freed objects for (i = 0; i < NUM_NASAL_TYPES; i++) { reap(&(globals->pools[i])); - printf(" p(%d)> %d", i, global_elapsedUSec()); } + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + printf(" >> reap %-5d", et); // Make enough space for the dead blocks we need to free during // execution. This works out to 1 spot for every 2 live objects, // which should be limit the number of bottleneck operations // without imposing an undue burden of extra "freeable" memory. if(globals->deadsz < globals->allocCount) { globals->deadsz = globals->allocCount; - if(globals->deadsz < 256) globals->deadsz = 256; + if(globals->deadsz < 256000) globals->deadsz = 256000; naFree(globals->deadBlocks); globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz); } globals->needGC = 0; - printf(">> %d ", global_elapsedUSec()); + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + printf(">> %-5d ", et); + busy = 0; } void naModLock() @@ -119,16 +173,33 @@ static void bottleneck() UNLOCK(); naSemDown(g->sem); LOCK(); g->waitCount--; } - printf("bottleneck wait finished %d usec", global_elapsedUSec()); + printf("GC: wait %2d ", global_elapsedUSec()); if(g->waitCount >= g->nThreads - 1) { - freeDead(); - //if(g->needGC) + int fd = freeDead(); + printf("--> freedead (%5d) : %5d", fd, global_elapsedUSec()); + if(g->needGC) garbageCollect(); if(g->waitCount) naSemUp(g->sem, g->waitCount); g->bottleneck = 0; } - char *c = getName(); - printf("bottleneck finished: %d %s\n", global_elapsedUSec(), c); + printf(" :: finished: %5d\n", global_elapsedUSec()); +} + +static void bottleneckFreeDead() +{ + global_stamp(); + struct Globals* g = globals; + g->bottleneck = 1; + while (g->bottleneck && g->waitCount < g->nThreads - 1) { + g->waitCount++; + UNLOCK(); naSemDown(g->sem); LOCK(); + g->waitCount--; + } + if (g->waitCount >= g->nThreads - 1) { + freeDead(); + if (g->waitCount) naSemUp(g->sem, g->waitCount); + g->bottleneck = 0; + } } void naGC() @@ -139,6 +210,29 @@ void naGC() UNLOCK(); naCheckBottleneck(); } +int naGarbageCollect() +{ + int rv = 1; + LOCK(); + // + // The number here is again based on observation - if this is too low then the inline GC will be used + // which is fine occasionally. + // So what we're doing by checking the global alloc is to see if GC is likely required during the next frame and if + // so we pre-empt this by doing it now. + // GC can typically take between 5ms and 50ms (F-15, FG1000 PFD & MFD, Advanced weather) - but usually it is completed + // prior to the start of the next frame. + + globals->needGC = nasal_globals->allocCount < 23000; + if (globals->needGC) + bottleneck(); + else { + bottleneckFreeDead(); + rv = 0; + } + UNLOCK(); + naCheckBottleneck(); + return rv; +} void naCheckBottleneck() { @@ -219,7 +313,9 @@ static int poolsize(struct naPool* p) while(b) { total += b->size; b = b->next; } return total; } - +int GCglobalAlloc() { + return globals->allocCount; +} struct naObj** naGC_get(struct naPool* p, int n, int* nout) { struct naObj** result; @@ -227,6 +323,7 @@ struct naObj** naGC_get(struct naPool* p, int n, int* nout) LOCK(); while(globals->allocCount < 0 || (p->nfree == 0 && p->freetop >= p->freesz)) { globals->needGC = 1; + printf("++"); bottleneck(); } if(p->nfree == 0) @@ -239,51 +336,130 @@ struct naObj** naGC_get(struct naPool* p, int n, int* nout) UNLOCK(); return result; } +extern void na_t_stack_push(naRef v); +extern int na_t_stack_count(); +extern naRef na_t_stack_pop(); -static void markvec(naRef r) +static void oldmarkvec(naRef r) { int i; struct VecRec* vr = PTR(r).vec->rec; - if(!vr) return; - for(i=0; isize; i++) + if (!vr) return; + for (i = 0; isize; i++) mark(vr->array[i]); } // Sets the reference bit on the object, and recursively on all // objects reachable from it. Uses the processor stack for recursion... -static void mark(naRef r) +static void oldmark(naRef r) { int i; - if(IS_NUM(r) || IS_NIL(r)) + if (IS_NUM(r) || IS_NIL(r)) return; - if(PTR(r).obj->mark == 1) + if (PTR(r).obj->mark == 1) return; PTR(r).obj->mark = 1; - switch(PTR(r).obj->type) { - case T_VEC: markvec(r); break; + switch (PTR(r).obj->type) { + case T_VEC: oldmarkvec(r); break; case T_HASH: naiGCMarkHash(r); break; case T_CODE: - mark(PTR(r).code->srcFile); - for(i=0; inConstants; i++) + oldmark(PTR(r).code->srcFile); + for (i = 0; inConstants; i++) mark(PTR(r).code->constants[i]); break; case T_FUNC: - mark(PTR(r).func->code); - mark(PTR(r).func->namespace); - mark(PTR(r).func->next); + oldmark(PTR(r).func->code); + oldmark(PTR(r).func->namespace); + oldmark(PTR(r).func->next); break; case T_GHOST: - mark(PTR(r).ghost->data); + oldmark(PTR(r).ghost->data); break; } } +void oldnaiGCMark(naRef r) +{ + oldmark(r); +} + +static int do_mark(naRef r) +{ + if (IS_NUM(r) || IS_NIL(r)) + return 1; + + if (PTR(r).obj->mark == 1) + return 1; + PTR(r).obj->mark = 1; + return 0; +} + +static void mark(naRef r) { + if (nasal_gc_old) + oldmark(r); + else + process_all(r, do_mark); +} + +static void process_all(naRef r, int (*process)(naRef r)) +{ + na_t_stack_push(r); + __elements_visited++; + while (na_t_stack_count() != 0) + { + naRef r = na_t_stack_pop(); + if ((*process)(r)) + continue; + + switch (PTR(r).obj->type) { + case T_VEC: { + int i; + struct VecRec* vr = PTR(r).vec->rec; + if (vr) { + for (i = 0; i < vr->size; i++) { + na_t_stack_push(vr->array[i]); + __elements_visited++; + } + } + break; + } + case T_HASH: naiGCMarkHash(r); break; + case T_CODE: + { + int i; + na_t_stack_push(PTR(r).code->srcFile); + for (i = 0; i < PTR(r).code->nConstants; i++) { + na_t_stack_push(PTR(r).code->constants[i]); + __elements_visited++; + } + break; + } + case T_FUNC: + __elements_visited++; + __elements_visited++; + __elements_visited++; + na_t_stack_push(PTR(r).func->code); + na_t_stack_push(PTR(r).func->namespace); + na_t_stack_push(PTR(r).func->next); + break; + case T_GHOST: + na_t_stack_push(PTR(r).ghost->data); + __elements_visited++; + break; + } + } +} void naiGCMark(naRef r) { - mark(r); + if (oldmark) + oldnaiGCMark(r); + else { + na_t_stack_push(r); + __elements_visited++; + } } // Collects all the unreachable objects into a free list, and @@ -304,9 +480,9 @@ static void reap(struct naPool* p) p->free = p->free0; for(b = p->blocks; b; b = b->next) - for(elem=0; elem < b->size; elem++) { + for (elem = 0; elem < b->size; elem++) { struct naObj* o = (struct naObj*)(b->block + elem * p->elemsz); - if(o->mark == 0) + if (o->mark == 0) freeelem(p, o); o->mark = 0; } @@ -318,11 +494,18 @@ static void reap(struct naPool* p) // Allocate more if necessary (try to keep 25-50% of the objects // available) - if(p->nfree < total/4) { + //if(p->nfree < total/4) { + // int used = total - p->nfree; + // int avail = total - used; + // int need = used/2 - avail; + // if(need > 0) + // newBlock(p, need); + //} + if (p->nfree < total / 2) { int used = total - p->nfree; int avail = total - used; - int need = used/2 - avail; - if(need > 0) + int need = used / 1 - avail; + if (need > 0) newBlock(p, need); } } diff --git a/simgear/nasal/hash.c b/simgear/nasal/hash.c index 3679a0ca..dee061da 100644 --- a/simgear/nasal/hash.c +++ b/simgear/nasal/hash.c @@ -176,6 +176,17 @@ void naiGCMarkHash(naRef hash) } } +void oldnaiGCMarkHash(naRef hash) +{ + int i; + HashRec* hr = REC(hash); + for (i = 0; hr && i < NCELLS(hr); i++) + if (TAB(hr)[i] >= 0) { + oldnaiGCMark(ENTS(hr)[TAB(hr)[i]].key); + oldnaiGCMark(ENTS(hr)[TAB(hr)[i]].val); + } +} + static void tmpStr(naRef* out, struct naStr* str, const char* key) { str->type = T_STR; diff --git a/simgear/nasal/misc.c b/simgear/nasal/misc.c index 764d113d..d2790cab 100644 --- a/simgear/nasal/misc.c +++ b/simgear/nasal/misc.c @@ -65,7 +65,7 @@ naRef naStringValue(naContext c, naRef r) naRef naNew(struct Context* c, int type) { - getSource(c); + //getSource(c); naRef result; if(c->nfree[type] == 0) c->free[type] = naGC_get(&globals->pools[type], From cde95864b459c833e024d2107b40a36384c3adeb Mon Sep 17 00:00:00 2001 From: Dan Wickstrom Date: Tue, 14 May 2019 12:51:35 -0400 Subject: [PATCH 11/39] Aircraft model reinit deletes sound effect samples, but leaves them defined in the sample group, so a reload doesn't re-add them. --- simgear/sound/xmlsound.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/simgear/sound/xmlsound.cxx b/simgear/sound/xmlsound.cxx index 7bb25c89..148c6b39 100644 --- a/simgear/sound/xmlsound.cxx +++ b/simgear/sound/xmlsound.cxx @@ -83,6 +83,9 @@ SGXmlSound::~SGXmlSound() if (_sample) _sample->stop(); + if (_sgrp && (_name != "")) + _sgrp->remove(_name); + _volume.clear(); _pitch.clear(); } From 4c52d77aa5f9735017c90bdb3becc080109a85ba Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Mon, 20 May 2019 11:00:51 +0200 Subject: [PATCH 12/39] Add missing cassert incldues --- simgear/structure/SGExpression.cxx | 1 + simgear/structure/subsystem_mgr.cxx | 1 + 2 files changed, 2 insertions(+) diff --git a/simgear/structure/SGExpression.cxx b/simgear/structure/SGExpression.cxx index 2633e311..e6b38500 100644 --- a/simgear/structure/SGExpression.cxx +++ b/simgear/structure/SGExpression.cxx @@ -33,6 +33,7 @@ #include #include #include // for strcmp +#include #include diff --git a/simgear/structure/subsystem_mgr.cxx b/simgear/structure/subsystem_mgr.cxx index bd990546..92daefcf 100644 --- a/simgear/structure/subsystem_mgr.cxx +++ b/simgear/structure/subsystem_mgr.cxx @@ -21,6 +21,7 @@ #include #include +#include #include #include From 81d1e16d7b728dd48909464c176c224be2437bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Mon, 20 May 2019 22:51:40 +0200 Subject: [PATCH 13/39] Compositor: Removed intersection checking This is now responsibility of the CameraGroup. --- simgear/scene/viewer/Compositor.cxx | 45 ----------------------------- simgear/scene/viewer/Compositor.hxx | 4 --- 2 files changed, 49 deletions(-) diff --git a/simgear/scene/viewer/Compositor.cxx b/simgear/scene/viewer/Compositor.cxx index 8d3bd9b7..964e387d 100644 --- a/simgear/scene/viewer/Compositor.cxx +++ b/simgear/scene/viewer/Compositor.cxx @@ -202,51 +202,6 @@ Compositor::resized() } } -bool -Compositor::computeIntersection( - const osg::Vec2d& windowPos, - osgUtil::LineSegmentIntersector::Intersections& intersections) -{ - using osgUtil::Intersector; - using osgUtil::LineSegmentIntersector; - - osg::Camera *camera = getPass(0)->camera; - const osg::Viewport* viewport = camera->getViewport(); - SGRect viewportRect(viewport->x(), viewport->y(), - viewport->x() + viewport->width() - 1.0, - viewport->y() + viewport->height()- 1.0); - - double epsilon = 0.5; - if (!viewportRect.contains(windowPos.x(), windowPos.y(), epsilon)) - return false; - - osg::Vec4d start(windowPos.x(), windowPos.y(), 0.0, 1.0); - osg::Vec4d end(windowPos.x(), windowPos.y(), 1.0, 1.0); - osg::Matrix windowMat = viewport->computeWindowMatrix(); - osg::Matrix startPtMat = osg::Matrix::inverse(camera->getProjectionMatrix() - * windowMat); - osg::Matrix endPtMat = startPtMat; // no far camera - - start = start * startPtMat; - start /= start.w(); - end = end * endPtMat; - end /= end.w(); - osg::ref_ptr picker - = new LineSegmentIntersector(Intersector::VIEW, - osg::Vec3d(start.x(), start.y(), start.z()), - osg::Vec3d(end.x(), end.y(), end.z())); - osgUtil::IntersectionVisitor iv(picker.get()); - iv.setTraversalMask( simgear::PICK_BIT ); - - const_cast(camera)->accept(iv); - if (picker->containsIntersections()) { - intersections = picker->getIntersections(); - return true; - } - - return false; -} - void Compositor::addBuffer(const std::string &name, Buffer *buffer) { diff --git a/simgear/scene/viewer/Compositor.hxx b/simgear/scene/viewer/Compositor.hxx index 63be4e8b..f2d0cc75 100644 --- a/simgear/scene/viewer/Compositor.hxx +++ b/simgear/scene/viewer/Compositor.hxx @@ -93,10 +93,6 @@ public: void resized(); - bool computeIntersection( - const osg::Vec2d& windowPos, - osgUtil::LineSegmentIntersector::Intersections& intersections); - osg::View *getView() const { return _view; } osg::GraphicsContext *getGraphicsContext() const { return _gc; } From b322fa8f32255fc12bed3b2ba926a70a9dbf13cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Mon, 20 May 2019 23:40:12 +0200 Subject: [PATCH 14/39] Use $FG_ROOT/Compositor/Effects instead of $FG_ROOT/Effects when the compositor is enabled This allows coexistence of new compositor-compatible effects and current effects. --- simgear/scene/material/makeEffect.cxx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/simgear/scene/material/makeEffect.cxx b/simgear/scene/material/makeEffect.cxx index cfbb87af..35591c51 100644 --- a/simgear/scene/material/makeEffect.cxx +++ b/simgear/scene/material/makeEffect.cxx @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -126,7 +127,12 @@ Effect* makeEffect(const string& name, itr->second.valid()) return itr->second.get(); } - string effectFileName(name); + string effectFileName; + // Use getPropertyRoot() because the SGReaderWriterOptions might not have a + // valid property tree + if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false)) + effectFileName += "Compositor/"; + effectFileName += name; effectFileName += ".eff"; string absFileName = SGModelLib::findDataFile(effectFileName, options); From 90845974ea26829aa167a9170ff87008eb41d688 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 23 Apr 2018 23:52:18 +0200 Subject: [PATCH 15/39] nasal/lib.c: Make copy of va_list for each traversal It's not portable to traverse a va_list more than once. --- simgear/nasal/lib.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/simgear/nasal/lib.c b/simgear/nasal/lib.c index 796c3ca3..528803b5 100644 --- a/simgear/nasal/lib.c +++ b/simgear/nasal/lib.c @@ -302,15 +302,18 @@ static char* dosprintf(char* f, ...) char* buf; va_list va; int olen, len = 16; + va_start(va, f); while(1) { buf = naAlloc(len); - va_start(va, f); - olen = vsnprintf(buf, len, f, va); + va_list vaCopy; + va_copy(vaCopy, va); + olen = vsnprintf(buf, len, f, vaCopy); if(olen >= 0 && olen < len) { va_end(va); + va_end(vaCopy); return buf; } - va_end(va); + va_end(vaCopy); naFree(buf); len *= 2; } From 5486ca3b4a5175f496321ef674819fff63b9a4a9 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 9 May 2018 09:27:16 +0200 Subject: [PATCH 16/39] Protect logstream startup entries with a mutex --- simgear/debug/logstream.cxx | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/simgear/debug/logstream.cxx b/simgear/debug/logstream.cxx index 93ba61b5..d5851001 100644 --- a/simgear/debug/logstream.cxx +++ b/simgear/debug/logstream.cxx @@ -425,8 +425,11 @@ public: return; } - m_startupLogging = on; - m_startupEntries.clear(); + { + SGGuard g(m_lock); + m_startupLogging = on; + m_startupEntries.clear(); + } } virtual void run() @@ -438,13 +441,14 @@ public: if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) { return; } - - if (m_startupLogging) { - // save to the startup list for not-yet-added callbacks to - // pull down on startup - m_startupEntries.push_back(entry); + { + SGGuard g(m_lock); + if (m_startupLogging) { + // save to the startup list for not-yet-added callbacks to + // pull down on startup + m_startupEntries.push_back(entry); + } } - // submit to each installed callback in turn for (simgear::LogCallback* cb : m_callbacks) { (*cb)(entry.debugClass, entry.debugPriority, @@ -455,14 +459,16 @@ public: bool stop() { - SGGuard g(m_lock); - if (!m_isRunning) { - return false; - } + { + SGGuard g(m_lock); + if (!m_isRunning) { + return false; + } - // log a special marker value, which will cause the thread to wakeup, - // and then exit - log(SG_NONE, SG_ALERT, "done", -1, ""); + // log a special marker value, which will cause the thread to wakeup, + // and then exit + log(SG_NONE, SG_ALERT, "done", -1, ""); + } join(); m_isRunning = false; From 9ac3c1a394d0d200a8f8276bc6298298e554ab71 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Mon, 27 May 2019 18:48:48 +0200 Subject: [PATCH 17/39] Revert GC instrumentation committed by mistake as part of "SGTimeStamp elapsedUSec" This reverts commit 0b114ac5cd8cc8854d4d2cb12212ce182a738ff9, reversing changes made to 39eb9837e9ced93eddc5c9a4ec9d35b8d981d2ad. --- simgear/nasal/code.c | 26 +++----------------------- simgear/nasal/gc.c | 22 +++++----------------- simgear/nasal/misc.c | 1 - simgear/timing/timestamp.cxx | 10 ---------- 4 files changed, 8 insertions(+), 51 deletions(-) diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index 740b7058..faf534ec 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -24,21 +24,6 @@ struct Globals* globals = 0; static naRef bindFunction(naContext ctx, struct Frame* f, naRef code); -char __name[3000] = { 0 }; -int init = 0; -void getSource(struct Context* c) { - naRef v = naGetSourceFile(c, 0); - init = 1; - if (!IS_NIL(v)) - snprintf(__name, 3000, "%s:%d", naStr_data(v), naGetLine(c, 0)); - else - *__name = 0; -} -char *getName() { - if (init) - return __name; - return "**"; -} #define ERR(c, msg) naRuntimeError((c),(msg)) void naRuntimeError(naContext c, const char* fmt, ...) { @@ -320,7 +305,6 @@ static void checkNamedArgs(naContext ctx, struct naCode* c, struct naHash* h) static struct Frame* setupFuncall(naContext ctx, int nargs, int mcall, int named) { - getSource(ctx); naRef *args, func, code, obj = naNil(); struct Frame* f; int opf = ctx->opTop - nargs; @@ -849,13 +833,9 @@ naRef naGetSourceFile(naContext ctx, int frame) { naRef f; frame = findFrame(ctx, &ctx, frame); - if (frame >= 0) { - f = ctx->fStack[frame].func; - f = PTR(f).func->code; - if (!IS_NIL(f) && PTR(f).code) - return PTR(f).code->srcFile; - } - return naNil(); + f = ctx->fStack[frame].func; + f = PTR(f).func->code; + return PTR(f).code->srcFile; } char* naGetError(naContext ctx) diff --git a/simgear/nasal/gc.c b/simgear/nasal/gc.c index 7c0a9183..5ac9c43c 100644 --- a/simgear/nasal/gc.c +++ b/simgear/nasal/gc.c @@ -1,6 +1,7 @@ #include "nasal.h" #include "data.h" #include "code.h" + #define MIN_BLOCK_SIZE 32 static void reap(struct naPool* p); @@ -11,17 +12,14 @@ struct Block { char* block; struct Block* next; }; + // Must be called with the giant exclusive lock! -extern void global_stamp(); -extern int global_elapsedUSec(); -extern char *getName(); static void freeDead() { int i; for(i=0; indead; i++) naFree(globals->deadBlocks[i]); globals->ndead = 0; - printf("--> freedead (%d) : %d", i, global_elapsedUSec()); } static void marktemps(struct Context* c) @@ -54,7 +52,6 @@ static void garbageCollect() marktemps(c); c = c->nextAll; } - printf("--> garbageCollect: %d ", global_elapsedUSec()); mark(globals->save); mark(globals->save_hash); @@ -63,13 +60,10 @@ static void garbageCollect() mark(globals->argRef); mark(globals->parentsRef); - printf("m> %d", global_elapsedUSec()); - // Finally collect all the freed objects - for (i = 0; i < NUM_NASAL_TYPES; i++) { + for(i=0; ipools[i])); - printf(" p(%d)> %d", i, global_elapsedUSec()); - } + // Make enough space for the dead blocks we need to free during // execution. This works out to 1 spot for every 2 live objects, // which should be limit the number of bottleneck operations @@ -81,7 +75,6 @@ static void garbageCollect() globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz); } globals->needGC = 0; - printf(">> %d ", global_elapsedUSec()); } void naModLock() @@ -111,7 +104,6 @@ void naModUnlock() // you think about it). static void bottleneck() { - global_stamp(); struct Globals* g = globals; g->bottleneck = 1; while(g->bottleneck && g->waitCount < g->nThreads - 1) { @@ -119,16 +111,12 @@ static void bottleneck() UNLOCK(); naSemDown(g->sem); LOCK(); g->waitCount--; } - printf("bottleneck wait finished %d usec", global_elapsedUSec()); if(g->waitCount >= g->nThreads - 1) { freeDead(); - //if(g->needGC) - garbageCollect(); + if(g->needGC) garbageCollect(); if(g->waitCount) naSemUp(g->sem, g->waitCount); g->bottleneck = 0; } - char *c = getName(); - printf("bottleneck finished: %d %s\n", global_elapsedUSec(), c); } void naGC() diff --git a/simgear/nasal/misc.c b/simgear/nasal/misc.c index 764d113d..0a5c8615 100644 --- a/simgear/nasal/misc.c +++ b/simgear/nasal/misc.c @@ -65,7 +65,6 @@ naRef naStringValue(naContext c, naRef r) naRef naNew(struct Context* c, int type) { - getSource(c); naRef result; if(c->nfree[type] == 0) c->free[type] = naGC_get(&globals->pools[type], diff --git a/simgear/timing/timestamp.cxx b/simgear/timing/timestamp.cxx index d9a2ef04..fcb56836 100644 --- a/simgear/timing/timestamp.cxx +++ b/simgear/timing/timestamp.cxx @@ -345,13 +345,3 @@ int SGTimeStamp::elapsedUSec() const return static_cast((now - *this).toUSecs()); } -extern "C" { - SGTimeStamp global_timestamp; - void global_stamp() { - global_timestamp.stamp(); - } - extern int global_elapsedUSec() - { - return global_timestamp.elapsedUSec(); - } - } \ No newline at end of file From 8eb51e813fd62098ba5c06c4746759187a8984f4 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Mon, 3 Jun 2019 23:32:34 +0200 Subject: [PATCH 18/39] Added Emesary to SimGear Core --- simgear/CMakeLists.txt | 1 + simgear/emesary/CMakeLists.txt | 28 +++ simgear/emesary/Emesary.cxx | 27 +++ simgear/emesary/Emesary.hxx | 325 ++++++++++++++++++++++++++++++ simgear/emesary/notifications.hxx | 76 +++++++ simgear/emesary/test_emesary.cxx | 126 ++++++++++++ 6 files changed, 583 insertions(+) create mode 100644 simgear/emesary/CMakeLists.txt create mode 100644 simgear/emesary/Emesary.cxx create mode 100644 simgear/emesary/Emesary.hxx create mode 100644 simgear/emesary/notifications.hxx create mode 100644 simgear/emesary/test_emesary.cxx diff --git a/simgear/CMakeLists.txt b/simgear/CMakeLists.txt index 6f8d2933..ca26c9d2 100644 --- a/simgear/CMakeLists.txt +++ b/simgear/CMakeLists.txt @@ -6,6 +6,7 @@ foreach( mylibfolder bvh debug embedded_resources + emesary ephemeris io magvar diff --git a/simgear/emesary/CMakeLists.txt b/simgear/emesary/CMakeLists.txt new file mode 100644 index 00000000..1003c6f0 --- /dev/null +++ b/simgear/emesary/CMakeLists.txt @@ -0,0 +1,28 @@ + + +include (SimGearComponent) + +set(HEADERS + emesary.hxx + notifications.hxx + ) + +set(SOURCES + emesary.cxx + ) + +simgear_component(emesary emesary "${SOURCES}" "${HEADERS}") + + +if(ENABLE_TESTS) + +add_executable(test_emesary test_emesary.cxx) + +set_target_properties(test_emesary PROPERTIES + COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" ) + +target_link_libraries(test_emesary ${TEST_LIBS}) +add_test(emesary ${EXECUTABLE_OUTPUT_PATH}/test_emesary) + + +endif(ENABLE_TESTS) diff --git a/simgear/emesary/Emesary.cxx b/simgear/emesary/Emesary.cxx new file mode 100644 index 00000000..bf91077c --- /dev/null +++ b/simgear/emesary/Emesary.cxx @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------- +* +* Title : Emesary - class based inter-object communication +* +* File Type : Implementation File +* +* Description : Templated version of Emesary +* : +* : +* : +* : +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002 +* +* Version : $Header: $ +* +* Copyright © 2002 Richard Harrison All Rights Reserved. +* +*---------------------------------------------------------------------------*/ + +#include "simgear/emesary/Emesary.hxx" + +simgear::Emesary::Transmitter GlobalTransmitter; diff --git a/simgear/emesary/Emesary.hxx b/simgear/emesary/Emesary.hxx new file mode 100644 index 00000000..da1f4ff8 --- /dev/null +++ b/simgear/emesary/Emesary.hxx @@ -0,0 +1,325 @@ +#pragma once +/*--------------------------------------------------------------------------- +* +* Title : Emesary - class based inter-object communication +* +* File Type : Implementation File +* +* Description : Provides generic inter-object communication. For an object to receive a message it +* : must first register with a Transmitter, such as GlobalTransmitter, and implement the +* : IReceiver interface. That's it. +* : To send a message use a Transmitter with an object. That's all there is to it. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017 +* +* Version : $Header: $ +* +* Copyright © 2002 - 2017 Richard Harrison All Rights Reserved. +* +*---------------------------------------------------------------------------*/ +#include + +#include +#include +#include +#include +#include +#include + + +namespace simgear +{ + namespace Emesary + { + enum ReceiptStatus + { + /// + /// Processing completed successfully + /// + ReceiptStatusOK = 0, + + /// + /// Individual item failure + /// + ReceiptStatusFail = 1, + + /// + /// Fatal error; stop processing any further recipieints of this message. Implicitly fail + /// + ReceiptStatusAbort = 2, + + /// + /// Definitive completion - do not send message to any further recipieints + /// + ReceiptStatusFinished = 3, + + /// + /// Return value when method doesn't process a message. + /// + ReceiptStatusNotProcessed = 4, + + /// + /// Message has been sent but the return status cannot be determined as it has not been processed by the recipient. + /// + /// + /// For example a queue or outgoing bridge + /// + ReceiptStatusPending = 5, + + /// + /// Message has been definitively handled but the return value cannot be determined. The message will not be sent any further + /// + /// + /// For example a point to point forwarding bridge + /// + ReceiptStatusPendingFinished = 6, + }; + + /// + /// Interface (base class) for all notifications. The value is an opaque pointer that may be used to store anything, although + /// often it is more convenient to + /// + class INotification + { + public: + virtual const char *GetType() = 0; + }; + /// + /// Interface (base class) for a recipeint. + /// + class IReceiver + { + public: + /// + /// Receive notifiction - must be implemented + /// + virtual ReceiptStatus Receive(INotification& message) = 0; + + /// + /// Called when registered at a transmitter + /// + virtual void OnRegisteredAtTransmitter(class Transmitter *p) + { + } + /// + /// Called when de-registered at a transmitter + /// + virtual void OnDeRegisteredAtTransmitter(class Transmitter *p) + { + } + }; + + /// + /// Interface (base clasee) for a transmitter. + /// Transmits Message derived objects. Each instance of this class provides a + /// databus to which any number of receivers can attach to. + /// + class ITransmitter + { + public: + /* + * Registers a recipient to receive message from this transmitter + */ + virtual void Register(IReceiver& R) = 0; + /* + * Removes a recipient from from this transmitter + */ + virtual void DeRegister(IReceiver& R) = 0; + + /* + * Notify all registered recipients. Stop when receipt status of abort or finished are received. + * The receipt status from this method will be + * - OK > message handled + * - Fail > message not handled. A status of Abort from a recipient will result in our status + * being fail as Abort means that the message was not and cannot be handled, and + * allows for usages such as access controls. + */ + virtual ReceiptStatus NotifyAll(INotification& M) = 0; + /// + /// number of recipients + /// + virtual int Count() = 0; + }; + + + /** + * Description: Transmits Message derived objects. Each instance of this class provides a + * databus to which any number of receivers can attach to. + * + * Messages may be inherited and customised between individual systems. + */ + class Transmitter : public ITransmitter + { + protected: + typedef std::list RecipientList; + RecipientList recipient_list; + RecipientList deleted_recipients; + int CurrentRecipientIndex = 0; + SGMutex _lock; + std::atomic receiveDepth; + std::atomic sentMessageCount; + + void UnlockList() + { + _lock.unlock(); + } + void LockList() + { + _lock.lock(); + } + public: + Transmitter() : receiveDepth(0), sentMessageCount(0) + { + } + virtual ~Transmitter() + { + } + /** + * Registers an object to receive messsages from this transmitter. + * This object is added to the top of the list of objects to be notified. This is deliberate as + * the sequence of registration and message receipt can influence the way messages are processing + * when ReceiptStatus of Abort or Finished are encountered. So it was a deliberate decision that the + * most recently registered recipients should process the messages/events first. + */ + virtual void Register(IReceiver& r) + { + LockList(); + recipient_list.push_back(&r); + r.OnRegisteredAtTransmitter(this); + if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end()) + deleted_recipients.remove(&r); + + UnlockList(); + } + + /* + * Removes an object from receving message from this transmitter + */ + virtual void DeRegister(IReceiver& R) + { + LockList(); + //printf("Remove %x\n", &R); + if (recipient_list.size()) + { + if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end()) + { + recipient_list.remove(&R); + R.OnDeRegisteredAtTransmitter(this); + if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &R) == deleted_recipients.end()) + deleted_recipients.push_back(&R); + } + } + UnlockList(); + } + + /* + * Notify all registered recipients. Stop when receipt status of abort or finished are received. + * The receipt status from this method will be + * - OK > message handled + * - Fail > message not handled. A status of Abort from a recipient will result in our status + * being fail as Abort means that the message was not and cannot be handled, and + * allows for usages such as access controls. + * NOTE: When I first designed Emesary I always intended to have message routing and the ability + * for each recipient to specify an area of interest to allow performance improvements + * however this has not yet been implemented - but the concept is still there and + * could be implemented by extending the IReceiver interface to allow for this. + */ + virtual ReceiptStatus NotifyAll(INotification& M) + { + ReceiptStatus return_status = ReceiptStatusNotProcessed; + //printf("Begin receive %d : %x\n", (int)receiveDepth, M); + //fflush(stdout); + sentMessageCount++; + try + { + LockList(); + if (receiveDepth == 0) + deleted_recipients.clear(); + receiveDepth++; + std::vector temp(recipient_list.size()); + int idx = 0; + for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++) + { + temp[idx++] = *i; + } + UnlockList(); + int tempSize = temp.size(); + for (int index = 0; index < tempSize; index++) + { + IReceiver* R = temp[index]; + LockList(); + if (deleted_recipients.size()) + { + if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end()) + { + UnlockList(); + continue; + } + } + UnlockList(); + if (R) + { + ReceiptStatus rstat = R->Receive(M); + switch (rstat) + { + case ReceiptStatusFail: + return_status = ReceiptStatusFail; + break; + case ReceiptStatusPending: + return_status = ReceiptStatusPending; + break; + case ReceiptStatusPendingFinished: + return rstat; + + case ReceiptStatusNotProcessed: + break; + case ReceiptStatusOK: + if (return_status == ReceiptStatusNotProcessed) + return_status = rstat; + break; + + case ReceiptStatusAbort: + return ReceiptStatusAbort; + + case ReceiptStatusFinished: + return ReceiptStatusOK; + } + } + + } + } + catch (...) + { + throw; + // return_status = ReceiptStatusAbort; + } + receiveDepth--; + //printf("End receive %d : %x\n", (int) receiveDepth, M); + return return_status; + } + virtual int Count() + { + LockList(); + return recipient_list.size(); + UnlockList(); + } + int SentMessageCount() + { + return sentMessageCount; + } + static bool Failed(ReceiptStatus receiptStatus) + { + // + // failed is either Fail or Abort. + // NotProcessed isn't a failure because it hasn't been processed. + return receiptStatus == ReceiptStatusFail + || receiptStatus == ReceiptStatusAbort; + } + }; + Transmitter GlobalTransmitter; + } +} \ No newline at end of file diff --git a/simgear/emesary/notifications.hxx b/simgear/emesary/notifications.hxx new file mode 100644 index 00000000..032d9252 --- /dev/null +++ b/simgear/emesary/notifications.hxx @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------- +* +* Title : Emesary - class based inter-object communication +* +* File Type : Implementation File +* +* Description : Provides generic inter-object communication. For an object to receive a message it +* : must first register with a Transmitter, such as GlobalTransmitter, and implement the +* : IReceiver interface. That's it. +* : To send a message use a Transmitter with an object. That's all there is to it. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017 +* +* Version : $Header: $ +* +* Copyright © 2002 - 2017 Richard Harrison All Rights Reserved. +* +*---------------------------------------------------------------------------*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace simgear +{ + namespace Notifications + { + class MainLoopNotification : public simgear::Emesary::INotification + { + public: + enum Type { Started, Stopped, Begin, End }; + MainLoopNotification(Type v) : Type(v) {} + + virtual Type GetValue() { return Type; } + virtual const char *GetType() { return "MainLoop"; } + + protected: + Type Type; + }; + + class NasalGarbageCollectionConfigurationNotification : public simgear::Emesary::INotification + { + public: + NasalGarbageCollectionConfigurationNotification(bool canWait, bool active) : CanWait(canWait), Active(active) {} + + virtual bool GetCanWait() { return CanWait; } + virtual bool GetActive() { return Active; } + virtual const char *GetType() { return "NasalGarbageCollectionConfiguration"; } + virtual bool SetWait(bool wait) { + if (wait == CanWait) + return false; + CanWait = wait; + return true; + } + virtual bool SetActive(bool active) { + if (active == Active) + return false; + Active = active; + return true; + } + public: + bool CanWait; + bool Active; + }; + } +} \ No newline at end of file diff --git a/simgear/emesary/test_emesary.cxx b/simgear/emesary/test_emesary.cxx new file mode 100644 index 00000000..ad9c190f --- /dev/null +++ b/simgear/emesary/test_emesary.cxx @@ -0,0 +1,126 @@ +//////////////////////////////////////////////////////////////////////// +// Test harness for Emesary. +//////////////////////////////////////////////////////////////////////// + +#include +#include + +#include + +#include + +using std::cout; +using std::cerr; +using std::endl; + +std::atomic nthread = 0; +std::atomic noperations = 0; +const int MaxIterations = 9999; + +class TestThreadNotification : public simgear::Emesary::INotification +{ +protected: + const char *baseValue; +public: + TestThreadNotification(const char *v) : baseValue(v) {} + + virtual const char* GetType () { return baseValue; } +}; + +class TestThreadRecipient : public simgear::Emesary::IReceiver +{ +public: + TestThreadRecipient() : receiveCount(0) + { + + } + + std::atomic receiveCount; + virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n) + { + if (n.GetType() == (const char*)this) + { + TestThreadNotification *tn = dynamic_cast(&n); + receiveCount++; + TestThreadNotification onwardNotification("AL"); + simgear::Emesary::GlobalTransmitter.NotifyAll(onwardNotification); + return simgear::Emesary::ReceiptStatusOK; + } + return simgear::Emesary::ReceiptStatusOK; + } +}; + +class EmesaryTestThread : public SGThread +{ +protected: + virtual void run() { + int threadId = nthread.fetch_add(1); + + //System.Threading.Interlocked.Increment(ref nthread); + //var rng = new Random(); + TestThreadRecipient r; + char temp[100]; + sprintf(temp, "Notif %d", threadId); + printf("starting thread %s\n", temp); + TestThreadNotification tn((const char*)&r); + for (int i = 0; i < MaxIterations; i++) + { + simgear::Emesary::GlobalTransmitter.Register(r); + simgear::Emesary::GlobalTransmitter.NotifyAll(tn); + simgear::Emesary::GlobalTransmitter.DeRegister(r); + //System.Threading.Thread.Sleep(rng.Next(MaxSleep)); + noperations++; + } + printf("%s invocations %d\n", temp, (int)r.receiveCount); + printf("finish thread %s\n", temp); + } +}; + +class EmesaryTest +{ +public: + + void Emesary_MultiThreadTransmitterTest() + { + int num_threads = 12; + std::list threads; + + for (int i = 0; i < num_threads; i++) + { + EmesaryTestThread *thread = new EmesaryTestThread(); + threads.push_back(thread); + thread->start(); + } + for (std::list::iterator i = threads.begin(); i != threads.end(); i++) + { + (*i)->join(); + } + } +}; + +void testEmesaryThreaded() +{ + TestThreadRecipient r; + TestThreadNotification tn((const char*)&r); + simgear::Emesary::GlobalTransmitter.Register(r); + for (int i = 0; i < MaxIterations*MaxIterations; i++) + { + simgear::Emesary::GlobalTransmitter.NotifyAll(tn); + //System.Threading.Thread.Sleep(rng.Next(MaxSleep)); + noperations++; + } + simgear::Emesary::GlobalTransmitter.DeRegister(r); + printf("invocations %d\n", simgear::Emesary::GlobalTransmitter.SentMessageCount()); + + EmesaryTest t; + t.Emesary_MultiThreadTransmitterTest(); +} + + +int main(int ac, char ** av) +{ + testEmesaryThreaded(); + + std::cout << "all tests passed" << std::endl; + return 0; +} From 798b90e0a58c3444275f139e74bfe62a50259dc2 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sat, 8 Jun 2019 10:12:22 +0200 Subject: [PATCH 19/39] Added Emesary --- simgear/emesary/Emesary.cxx | 16 +- simgear/emesary/Emesary.hxx | 326 ++------------------------- simgear/emesary/INotification.hxx | 54 +++++ simgear/emesary/IReceiver.hxx | 47 ++++ simgear/emesary/ITransmitter.hxx | 52 +++++ simgear/emesary/ReceiptStatus.hxx | 54 +++++ simgear/emesary/Transmitter.hxx | 202 +++++++++++++++++ simgear/emesary/notifications.hxx | 20 +- simgear/nasal/cppbind/CMakeLists.txt | 1 + 9 files changed, 447 insertions(+), 325 deletions(-) create mode 100644 simgear/emesary/INotification.hxx create mode 100644 simgear/emesary/IReceiver.hxx create mode 100644 simgear/emesary/ITransmitter.hxx create mode 100644 simgear/emesary/ReceiptStatus.hxx create mode 100644 simgear/emesary/Transmitter.hxx diff --git a/simgear/emesary/Emesary.cxx b/simgear/emesary/Emesary.cxx index bf91077c..31b5f0ab 100644 --- a/simgear/emesary/Emesary.cxx +++ b/simgear/emesary/Emesary.cxx @@ -4,11 +4,9 @@ * * File Type : Implementation File * -* Description : Templated version of Emesary -* : -* : -* : -* : +* Description : Emesary main. +* : This only needs to instance the GlobalTransmitter as all of the +* : logic is in the header files (by design) * * References : http://www.chateau-logic.com/content/class-based-inter-object-communication * @@ -24,4 +22,10 @@ #include "simgear/emesary/Emesary.hxx" -simgear::Emesary::Transmitter GlobalTransmitter; +namespace simgear +{ + namespace Emesary + { + Transmitter GlobalTransmitter; + } +} diff --git a/simgear/emesary/Emesary.hxx b/simgear/emesary/Emesary.hxx index da1f4ff8..e03162df 100644 --- a/simgear/emesary/Emesary.hxx +++ b/simgear/emesary/Emesary.hxx @@ -1,325 +1,41 @@ -#pragma once +#ifndef EMESARY_hxx +#define EMESARY_hxx /*--------------------------------------------------------------------------- * -* Title : Emesary - class based inter-object communication +* Title : Emesary - class based inter-object communication * -* File Type : Implementation File +* File Type : Implementation File * -* Description : Provides generic inter-object communication. For an object to receive a message it -* : must first register with a Transmitter, such as GlobalTransmitter, and implement the -* : IReceiver interface. That's it. -* : To send a message use a Transmitter with an object. That's all there is to it. +* Description : Provides generic inter-object communication. For an object to receive a message it +* : must first register with a Transmitter, such as GlobalTransmitter, and implement the +* : IReceiver interface. That's it. +* : To send a message use a Transmitter with an object. That's all there is to it. * * References : http://www.chateau-logic.com/content/class-based-inter-object-communication * -* Author : Richard Harrison (richard@zaretto.com) +* Author : Richard Harrison (richard@zaretto.com) * -* Creation Date : 18 March 2002, rewrite 2017 +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 * -* Version : $Header: $ +* Version : $Header: $ * -* Copyright © 2002 - 2017 Richard Harrison All Rights Reserved. +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. * *---------------------------------------------------------------------------*/ #include -#include -#include -#include -#include -#include -#include - +#include "ReceiptStatus.hxx" +#include "INotification.hxx" +#include "IReceiver.hxx" +#include "ITransmitter.hxx" +#include "Transmitter.hxx" namespace simgear { namespace Emesary { - enum ReceiptStatus - { - /// - /// Processing completed successfully - /// - ReceiptStatusOK = 0, - - /// - /// Individual item failure - /// - ReceiptStatusFail = 1, - - /// - /// Fatal error; stop processing any further recipieints of this message. Implicitly fail - /// - ReceiptStatusAbort = 2, - - /// - /// Definitive completion - do not send message to any further recipieints - /// - ReceiptStatusFinished = 3, - - /// - /// Return value when method doesn't process a message. - /// - ReceiptStatusNotProcessed = 4, - - /// - /// Message has been sent but the return status cannot be determined as it has not been processed by the recipient. - /// - /// - /// For example a queue or outgoing bridge - /// - ReceiptStatusPending = 5, - - /// - /// Message has been definitively handled but the return value cannot be determined. The message will not be sent any further - /// - /// - /// For example a point to point forwarding bridge - /// - ReceiptStatusPendingFinished = 6, - }; - - /// - /// Interface (base class) for all notifications. The value is an opaque pointer that may be used to store anything, although - /// often it is more convenient to - /// - class INotification - { - public: - virtual const char *GetType() = 0; - }; - /// - /// Interface (base class) for a recipeint. - /// - class IReceiver - { - public: - /// - /// Receive notifiction - must be implemented - /// - virtual ReceiptStatus Receive(INotification& message) = 0; - - /// - /// Called when registered at a transmitter - /// - virtual void OnRegisteredAtTransmitter(class Transmitter *p) - { - } - /// - /// Called when de-registered at a transmitter - /// - virtual void OnDeRegisteredAtTransmitter(class Transmitter *p) - { - } - }; - - /// - /// Interface (base clasee) for a transmitter. - /// Transmits Message derived objects. Each instance of this class provides a - /// databus to which any number of receivers can attach to. - /// - class ITransmitter - { - public: - /* - * Registers a recipient to receive message from this transmitter - */ - virtual void Register(IReceiver& R) = 0; - /* - * Removes a recipient from from this transmitter - */ - virtual void DeRegister(IReceiver& R) = 0; - - /* - * Notify all registered recipients. Stop when receipt status of abort or finished are received. - * The receipt status from this method will be - * - OK > message handled - * - Fail > message not handled. A status of Abort from a recipient will result in our status - * being fail as Abort means that the message was not and cannot be handled, and - * allows for usages such as access controls. - */ - virtual ReceiptStatus NotifyAll(INotification& M) = 0; - /// - /// number of recipients - /// - virtual int Count() = 0; - }; - - - /** - * Description: Transmits Message derived objects. Each instance of this class provides a - * databus to which any number of receivers can attach to. - * - * Messages may be inherited and customised between individual systems. - */ - class Transmitter : public ITransmitter - { - protected: - typedef std::list RecipientList; - RecipientList recipient_list; - RecipientList deleted_recipients; - int CurrentRecipientIndex = 0; - SGMutex _lock; - std::atomic receiveDepth; - std::atomic sentMessageCount; - - void UnlockList() - { - _lock.unlock(); - } - void LockList() - { - _lock.lock(); - } - public: - Transmitter() : receiveDepth(0), sentMessageCount(0) - { - } - virtual ~Transmitter() - { - } - /** - * Registers an object to receive messsages from this transmitter. - * This object is added to the top of the list of objects to be notified. This is deliberate as - * the sequence of registration and message receipt can influence the way messages are processing - * when ReceiptStatus of Abort or Finished are encountered. So it was a deliberate decision that the - * most recently registered recipients should process the messages/events first. - */ - virtual void Register(IReceiver& r) - { - LockList(); - recipient_list.push_back(&r); - r.OnRegisteredAtTransmitter(this); - if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end()) - deleted_recipients.remove(&r); - - UnlockList(); - } - - /* - * Removes an object from receving message from this transmitter - */ - virtual void DeRegister(IReceiver& R) - { - LockList(); - //printf("Remove %x\n", &R); - if (recipient_list.size()) - { - if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end()) - { - recipient_list.remove(&R); - R.OnDeRegisteredAtTransmitter(this); - if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &R) == deleted_recipients.end()) - deleted_recipients.push_back(&R); - } - } - UnlockList(); - } - - /* - * Notify all registered recipients. Stop when receipt status of abort or finished are received. - * The receipt status from this method will be - * - OK > message handled - * - Fail > message not handled. A status of Abort from a recipient will result in our status - * being fail as Abort means that the message was not and cannot be handled, and - * allows for usages such as access controls. - * NOTE: When I first designed Emesary I always intended to have message routing and the ability - * for each recipient to specify an area of interest to allow performance improvements - * however this has not yet been implemented - but the concept is still there and - * could be implemented by extending the IReceiver interface to allow for this. - */ - virtual ReceiptStatus NotifyAll(INotification& M) - { - ReceiptStatus return_status = ReceiptStatusNotProcessed; - //printf("Begin receive %d : %x\n", (int)receiveDepth, M); - //fflush(stdout); - sentMessageCount++; - try - { - LockList(); - if (receiveDepth == 0) - deleted_recipients.clear(); - receiveDepth++; - std::vector temp(recipient_list.size()); - int idx = 0; - for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++) - { - temp[idx++] = *i; - } - UnlockList(); - int tempSize = temp.size(); - for (int index = 0; index < tempSize; index++) - { - IReceiver* R = temp[index]; - LockList(); - if (deleted_recipients.size()) - { - if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end()) - { - UnlockList(); - continue; - } - } - UnlockList(); - if (R) - { - ReceiptStatus rstat = R->Receive(M); - switch (rstat) - { - case ReceiptStatusFail: - return_status = ReceiptStatusFail; - break; - case ReceiptStatusPending: - return_status = ReceiptStatusPending; - break; - case ReceiptStatusPendingFinished: - return rstat; - - case ReceiptStatusNotProcessed: - break; - case ReceiptStatusOK: - if (return_status == ReceiptStatusNotProcessed) - return_status = rstat; - break; - - case ReceiptStatusAbort: - return ReceiptStatusAbort; - - case ReceiptStatusFinished: - return ReceiptStatusOK; - } - } - - } - } - catch (...) - { - throw; - // return_status = ReceiptStatusAbort; - } - receiveDepth--; - //printf("End receive %d : %x\n", (int) receiveDepth, M); - return return_status; - } - virtual int Count() - { - LockList(); - return recipient_list.size(); - UnlockList(); - } - int SentMessageCount() - { - return sentMessageCount; - } - static bool Failed(ReceiptStatus receiptStatus) - { - // - // failed is either Fail or Abort. - // NotProcessed isn't a failure because it hasn't been processed. - return receiptStatus == ReceiptStatusFail - || receiptStatus == ReceiptStatusAbort; - } - }; - Transmitter GlobalTransmitter; + // default system wide instance of transmitter object. + extern Transmitter GlobalTransmitter; } -} \ No newline at end of file +} +#endif diff --git a/simgear/emesary/INotification.hxx b/simgear/emesary/INotification.hxx new file mode 100644 index 00000000..0d6a517a --- /dev/null +++ b/simgear/emesary/INotification.hxx @@ -0,0 +1,54 @@ +#ifndef INOTIFICATION_hxx +#define INOTIFICATION_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Notification base class +* +* File Type : Implementation File +* +* Description : Base class (interface) for all Notifications. +* : This is also compatible with the usual implementation of how we +* : implement queued notifications. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ +namespace simgear +{ + namespace Emesary + { + /// Interface (base class) for all notifications. + class INotification + { + public: + // text representation of notification type. must be unique across all notifications + virtual const char *GetType() = 0; + + /// Used to control the sending of notifications. If this returns false then the Transmitter + /// should not send this notification. + virtual bool IsReadyToSend() { return true; } + + /// Used to control the timeout. If this notification has timed out - then the processor is entitled + /// to true. + virtual bool IsTimedOut() { return false; } + + /// when this notification has completed the processing recipient must set this to true. + /// the processing recipient is responsible for follow on notifications. + /// a notification can remain as complete until the transmit queue decides to remove it from the queue. + /// there is no requirement that elements are removed immediately upon completion merely that once complete + /// the transmitter should not notify any more elements. + /// The current notification loop may be completed - following the usual convention unless Completed or Abort + /// is returned as the status. + virtual bool IsComplete() { return true; } + }; + } +} +#endif diff --git a/simgear/emesary/IReceiver.hxx b/simgear/emesary/IReceiver.hxx new file mode 100644 index 00000000..f21a8a7c --- /dev/null +++ b/simgear/emesary/IReceiver.hxx @@ -0,0 +1,47 @@ +#ifndef IRECEIVER_hxx +#define IRECEIVER_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Receiver base class +* +* File Type : Implementation File +* +* Description : Base class for all recipients. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ +namespace simgear +{ + namespace Emesary + { + + /// Interface (base class) for a recipeint. + class IReceiver + { + public: + /// Receive notification - must be implemented + virtual ReceiptStatus Receive(INotification& message) = 0; + + /// Called when registered at a transmitter + virtual void OnRegisteredAtTransmitter(class Transmitter *p) + { + } + + /// Called when de-registered at a transmitter + virtual void OnDeRegisteredAtTransmitter(class Transmitter *p) + { + } + }; + + } +} +#endif \ No newline at end of file diff --git a/simgear/emesary/ITransmitter.hxx b/simgear/emesary/ITransmitter.hxx new file mode 100644 index 00000000..cd4f750c --- /dev/null +++ b/simgear/emesary/ITransmitter.hxx @@ -0,0 +1,52 @@ +#ifndef ITRANSMITTER_hxx +#define ITRANSMITTER_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Transmitter base class +* +* File Type : Implementation File +* +* Description : Base class for all transmitters. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ + +namespace simgear +{ + namespace Emesary + { + /// Interface (base clasee) for a transmitter. + /// Transmits Message derived objects. Each instance of this class provides a + /// event/databus to which any number of receivers can attach to. + class ITransmitter + { + public: + // Registers a recipient to receive message from this transmitter + virtual void Register(IReceiver& R) = 0; + // Removes a recipient from from this transmitter + virtual void DeRegister(IReceiver& R) = 0; + + + //Notify all registered recipients. Stop when receipt status of abort or finished are received. + //The receipt status from this method will be + // - OK > message handled + // - Fail > message not handled. A status of Abort from a recipient will result in our status + // being fail as Abort means that the message was not and cannot be handled, and + // allows for usages such as access controls. + virtual ReceiptStatus NotifyAll(INotification& M) = 0; + + /// number of recipients + virtual int Count() = 0; + }; + } +} +#endif diff --git a/simgear/emesary/ReceiptStatus.hxx b/simgear/emesary/ReceiptStatus.hxx new file mode 100644 index 00000000..4620e00e --- /dev/null +++ b/simgear/emesary/ReceiptStatus.hxx @@ -0,0 +1,54 @@ +#ifndef RECEIPTSTATUS_hxx +#define RECEIPTSTATUS_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Transmitter base class +* +* File Type : Implementation File +* +* Description : Defines the receipt status that can be returned from +* : a receive method. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ +namespace simgear +{ + namespace Emesary + { + enum ReceiptStatus + { + /// Processing completed successfully + ReceiptStatusOK = 0, + + /// Individual item failure + ReceiptStatusFail = 1, + + /// Fatal error; stop processing any further recipieints of this message. Implicitly fail + ReceiptStatusAbort = 2, + + /// Definitive completion - do not send message to any further recipieints + ReceiptStatusFinished = 3, + + /// Return value when method doesn't process a message. + ReceiptStatusNotProcessed = 4, + + /// Message has been sent but the return status cannot be determined as it has not been processed by the recipient. + /// e.g. a queue or outgoing bridge + ReceiptStatusPending = 5, + + /// Message has been definitively handled but the return value cannot be determined. The message will not be sent any further + /// e.g. a point to point forwarding bridge + ReceiptStatusPendingFinished = 6, + }; + } +} +#endif diff --git a/simgear/emesary/Transmitter.hxx b/simgear/emesary/Transmitter.hxx new file mode 100644 index 00000000..cb67bc64 --- /dev/null +++ b/simgear/emesary/Transmitter.hxx @@ -0,0 +1,202 @@ +#ifndef TRANSMITTER_hxx +#define TRANSMITTER_hxx +/*--------------------------------------------------------------------------- +* +* Title : Emesary - Transmitter base class +* +* File Type : Implementation File +* +* Description : Defines the receipt status that can be returned from +* : a receive method. +* +* References : http://www.chateau-logic.com/content/class-based-inter-object-communication +* +* Author : Richard Harrison (richard@zaretto.com) +* +* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019 +* +* Version : $Header: $ +* +* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later. +* +*---------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include + +namespace simgear +{ + namespace Emesary + { + // Implementation of a ITransmitter + class Transmitter : public ITransmitter + { + protected: + typedef std::list RecipientList; + RecipientList recipient_list; + RecipientList deleted_recipients; + int CurrentRecipientIndex = 0; + SGMutex _lock; + std::atomic receiveDepth; + std::atomic sentMessageCount; + + void UnlockList() + { + _lock.unlock(); + } + void LockList() + { + _lock.lock(); + } + public: + Transmitter() : receiveDepth(0), sentMessageCount(0) + { + } + + virtual ~Transmitter() + { + } + + // Registers an object to receive messsages from this transmitter. + // This object is added to the top of the list of objects to be notified. This is deliberate as + // the sequence of registration and message receipt can influence the way messages are processing + // when ReceiptStatus of Abort or Finished are encountered. So it was a deliberate decision that the + // most recently registered recipients should process the messages/events first. + virtual void Register(IReceiver& r) + { + LockList(); + recipient_list.push_back(&r); + r.OnRegisteredAtTransmitter(this); + if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end()) + deleted_recipients.remove(&r); + + UnlockList(); + } + + // Removes an object from receving message from this transmitter + virtual void DeRegister(IReceiver& R) + { + LockList(); + //printf("Remove %x\n", &R); + if (recipient_list.size()) + { + if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end()) + { + recipient_list.remove(&R); + R.OnDeRegisteredAtTransmitter(this); + if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &R) == deleted_recipients.end()) + deleted_recipients.push_back(&R); + } + } + UnlockList(); + } + + // Notify all registered recipients. Stop when receipt status of abort or finished are received. + // The receipt status from this method will be + // - OK > message handled + // - Fail > message not handled. A status of Abort from a recipient will result in our status + // being fail as Abort means that the message was not and cannot be handled, and + // allows for usages such as access controls. + virtual ReceiptStatus NotifyAll(INotification& M) + { + ReceiptStatus return_status = ReceiptStatusNotProcessed; + + sentMessageCount++; + try + { + LockList(); + if (receiveDepth == 0) + deleted_recipients.clear(); + receiveDepth++; + std::vector temp(recipient_list.size()); + int idx = 0; + for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++) + { + temp[idx++] = *i; + } + UnlockList(); + int tempSize = temp.size(); + for (int index = 0; index < tempSize; index++) + { + IReceiver* R = temp[index]; + LockList(); + if (deleted_recipients.size()) + { + if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end()) + { + UnlockList(); + continue; + } + } + UnlockList(); + if (R) + { + ReceiptStatus rstat = R->Receive(M); + switch (rstat) + { + case ReceiptStatusFail: + return_status = ReceiptStatusFail; + break; + case ReceiptStatusPending: + return_status = ReceiptStatusPending; + break; + case ReceiptStatusPendingFinished: + return rstat; + + case ReceiptStatusNotProcessed: + break; + case ReceiptStatusOK: + if (return_status == ReceiptStatusNotProcessed) + return_status = rstat; + break; + + case ReceiptStatusAbort: + return ReceiptStatusAbort; + + case ReceiptStatusFinished: + return ReceiptStatusOK; + } + } + + } + } + catch (...) + { + throw; + // return_status = ReceiptStatusAbort; + } + receiveDepth--; + return return_status; + } + + // number of currently registered recipients + virtual int Count() + { + LockList(); + return recipient_list.size(); + UnlockList(); + } + + // number of sent messages. + int SentMessageCount() + { + return sentMessageCount; + } + + // ascertain if a receipt status can be interpreted as failure. + static bool Failed(ReceiptStatus receiptStatus) + { + // + // failed is either Fail or Abort. + // NotProcessed isn't a failure because it hasn't been processed. + return receiptStatus == ReceiptStatusFail + || receiptStatus == ReceiptStatusAbort; + } + }; + } +} +#endif diff --git a/simgear/emesary/notifications.hxx b/simgear/emesary/notifications.hxx index 032d9252..bc8fd12e 100644 --- a/simgear/emesary/notifications.hxx +++ b/simgear/emesary/notifications.hxx @@ -1,13 +1,12 @@ +#ifndef NOTIFICATIONS_hxx +#define NOTIFICATIONS_hxx /*--------------------------------------------------------------------------- * * Title : Emesary - class based inter-object communication * * File Type : Implementation File * -* Description : Provides generic inter-object communication. For an object to receive a message it -* : must first register with a Transmitter, such as GlobalTransmitter, and implement the -* : IReceiver interface. That's it. -* : To send a message use a Transmitter with an object. That's all there is to it. +* Description : simgear notifications * * References : http://www.chateau-logic.com/content/class-based-inter-object-communication * @@ -20,16 +19,8 @@ * Copyright © 2002 - 2017 Richard Harrison All Rights Reserved. * *---------------------------------------------------------------------------*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "INotification.hxx" namespace simgear { @@ -73,4 +64,5 @@ namespace simgear bool Active; }; } -} \ No newline at end of file +} +#endif diff --git a/simgear/nasal/cppbind/CMakeLists.txt b/simgear/nasal/cppbind/CMakeLists.txt index 08f09937..37e10f72 100644 --- a/simgear/nasal/cppbind/CMakeLists.txt +++ b/simgear/nasal/cppbind/CMakeLists.txt @@ -27,6 +27,7 @@ set(SOURCES NasalHash.cxx NasalString.cxx NasalObject.cxx + NasalEmesaryInterface.cxx detail/from_nasal_helper.cxx detail/to_nasal_helper.cxx ) From a68c4e943424ffb1938994f45187de80e57ead33 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sat, 8 Jun 2019 15:38:22 +0200 Subject: [PATCH 20/39] DDS Texture cache improvements - change to always use the SG create mipmap function as it works better (no purple clouds) - when enabled always generate a mipmap even if the file couldn't be compressed - adjust level of log messages - move lru_cache into its own header file. --- simgear/misc/CMakeLists.txt | 1 + simgear/misc/lru_cache.hxx | 173 ++++++++++++++ simgear/scene/model/ModelRegistry.cxx | 320 ++++++-------------------- 3 files changed, 247 insertions(+), 247 deletions(-) create mode 100644 simgear/misc/lru_cache.hxx diff --git a/simgear/misc/CMakeLists.txt b/simgear/misc/CMakeLists.txt index 2931a3b3..33cc4a52 100644 --- a/simgear/misc/CMakeLists.txt +++ b/simgear/misc/CMakeLists.txt @@ -19,6 +19,7 @@ set(HEADERS tabbed_values.hxx texcoord.hxx test_macros.hxx + lru_cache.hxx ) set(SOURCES diff --git a/simgear/misc/lru_cache.hxx b/simgear/misc/lru_cache.hxx new file mode 100644 index 00000000..92f85b57 --- /dev/null +++ b/simgear/misc/lru_cache.hxx @@ -0,0 +1,173 @@ +///@file +/// Compare lists and get differences +//---------------------------------------------------------------------------// +// Copyright (c) 2013 Kyle Lutz +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +// See http://boostorg.github.com/compute for more information. +//---------------------------------------------------------------------------// +/// +// Changes Copyright (C) 2019 Richard Harrison (rjh@zaretto.com) +// +// As the boost licence is lax and permissive see +// (https://www.gnu.org/licenses/license-list.en.html#boost) +// any changes to this module are covered under the GPL +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef LRU_CACHE_HXX_ +#define LRU_CACHE_HXX_ + +#include +#include +#include +#include + +#include +#include + +namespace simgear +{ + // a cache which evicts the least recently used item when it is full + template + class lru_cache + { + public: + SGMutex _mutex; + + typedef Key key_type; + typedef Value value_type; + typedef std::list list_type; + typedef std::map< + key_type, + std::pair + > map_type; + + lru_cache(size_t capacity) + : m_capacity(capacity) + { + } + + ~lru_cache() + { + } + + + size_t size() const + { + return m_map.size(); + } + + size_t capacity() const + { + return m_capacity; + } + + bool empty() const + { + return m_map.empty(); + } + + bool contains(const key_type &key) + { + SGGuard scopeLock(_mutex); + return m_map.find(key) != m_map.end(); + } + + void insert(const key_type &key, const value_type &value) + { + SGGuard scopeLock(_mutex); + typename map_type::iterator i = m_map.find(key); + if (i == m_map.end()) { + // insert item into the cache, but first check if it is full + if (size() >= m_capacity) { + // cache is full, evict the least recently used item + evict(); + } + + // insert the new item + m_list.push_front(key); + m_map[key] = std::make_pair(value, m_list.begin()); + } + } + boost::optional findValue(const std::string &requiredValue) + { + SGGuard scopeLock(_mutex); + for (typename map_type::iterator it = m_map.begin(); it != m_map.end(); ++it) + if (it->second.first == requiredValue) + return it->first; + return boost::none; + } + boost::optional get(const key_type &key) + { + SGGuard scopeLock(_mutex); + // lookup value in the cache + typename map_type::iterator i = m_map.find(key); + if (i == m_map.end()) { + // value not in cache + return boost::none; + } + + // return the value, but first update its place in the most + // recently used list + typename list_type::iterator j = i->second.second; + if (j != m_list.begin()) { + // move item to the front of the most recently used list + m_list.erase(j); + m_list.push_front(key); + + // update iterator in map + j = m_list.begin(); + const value_type &value = i->second.first; + m_map[key] = std::make_pair(value, j); + + // return the value + return value; + } + else { + // the item is already at the front of the most recently + // used list so just return it + return i->second.first; + } + } + + void clear() + { + SGGuard scopeLock(_mutex); + m_map.clear(); + m_list.clear(); + } + + private: + void evict() + { + SGGuard scopeLock(_mutex); + // evict item from the end of most recently used list + typename list_type::iterator i = --m_list.end(); + m_map.erase(*i); + m_list.erase(i); + } + + private: + map_type m_map; + list_type m_list; + size_t m_capacity; + }; +} // namespace simgear + +#endif /* SG_LISTDIFF_HXX_ */ diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx index 651a4ad7..0087cbbf 100644 --- a/simgear/scene/model/ModelRegistry.cxx +++ b/simgear/scene/model/ModelRegistry.cxx @@ -62,6 +62,7 @@ #include #include #include +#include #include "BoundingVolumeBuildVisitor.hxx" #include "model.hxx" @@ -182,7 +183,7 @@ public: } // namespace -static int nearestPowerOfTwo(unsigned int _v) +static int nearestPowerOfTwo(int _v) { // uint v; // compute the next highest power of 2 of 32-bit v unsigned int v = (unsigned int)_v; @@ -207,10 +208,12 @@ static int nearestPowerOfTwo(unsigned int _v) _v = (int)v; return v; } - static bool isPowerOfTwo(int v) + +static bool isPowerOfTwo(int v) { return ((v & (v - 1)) == 0); } + osg::Node* DefaultProcessPolicy::process(osg::Node* node, const std::string& filename, const Options* opt) { @@ -232,144 +235,28 @@ osg::Image* getImageByName(const std::string& filename) return nullptr; } #endif -// a cache which evicts the least recently used item when it is full -#include -#include -#include -#include -template -class lru_cache -{ -public: - SGMutex _mutex; +// least recently used cache to speed up the process of finding a file +// after the first time - otherwise each time we'll have to generate a hash +// of the contents. +static lru_cache < std::string, std::string> filename_hash_cache(100000); - typedef Key key_type; - typedef Value value_type; - typedef std::list list_type; - typedef std::map< - key_type, - std::pair - > map_type; - - lru_cache(size_t capacity) - : m_capacity(capacity) - { - } - - ~lru_cache() - { - } - - - size_t size() const - { - return m_map.size(); - } - - size_t capacity() const - { - return m_capacity; - } - - bool empty() const - { - return m_map.empty(); - } - - bool contains(const key_type &key) - { - SGGuard scopeLock(_mutex); - return m_map.find(key) != m_map.end(); - } - - void insert(const key_type &key, const value_type &value) - { - SGGuard scopeLock(_mutex); - typename map_type::iterator i = m_map.find(key); - if (i == m_map.end()) { - // insert item into the cache, but first check if it is full - if (size() >= m_capacity) { - // cache is full, evict the least recently used item - evict(); - } - - // insert the new item - m_list.push_front(key); - m_map[key] = std::make_pair(value, m_list.begin()); - } - } - boost::optional findValue(const std::string &requiredValue) - { - SGGuard scopeLock(_mutex); - for (typename map_type::iterator it = m_map.begin(); it != m_map.end(); ++it) - if (it->second.first == requiredValue) - return it->first; - return boost::none; - } - boost::optional get(const key_type &key) - { - SGGuard scopeLock(_mutex); - // lookup value in the cache - typename map_type::iterator i = m_map.find(key); - if (i == m_map.end()) { - // value not in cache - return boost::none; - } - - // return the value, but first update its place in the most - // recently used list - typename list_type::iterator j = i->second.second; - if (j != m_list.begin()) { - // move item to the front of the most recently used list - m_list.erase(j); - m_list.push_front(key); - - // update iterator in map - j = m_list.begin(); - const value_type &value = i->second.first; - m_map[key] = std::make_pair(value, j); - - // return the value - return value; - } - else { - // the item is already at the front of the most recently - // used list so just return it - return i->second.first; - } - } - - void clear() - { - SGGuard scopeLock(_mutex); - m_map.clear(); - m_list.clear(); - } - -private: - void evict() - { - SGGuard scopeLock(_mutex); - // evict item from the end of most recently used list - typename list_type::iterator i = --m_list.end(); - m_map.erase(*i); - m_list.erase(i); - } - -private: - map_type m_map; - list_type m_list; - size_t m_capacity; -}; -lru_cache < std::string, std::string> filename_hash_cache(100000); -lru_cache < std::string, bool> filesCleaned(100000); -static bool refreshCache = false; +// experimental (incomplete) features to allow maintenance of the filecache. +//lru_cache < std::string, bool> filesCleaned(100000); +//static bool refreshCache = false; ReaderWriter::ReadResult ModelRegistry::readImage(const string& fileName, const Options* opt) { + // experimental feature to see if we can reload textures during model load + // as otherwise texture creation/editting requires a restart or a change to + // a different filenaem + //if (SGSceneFeatures::instance()->getReloadCache()) { + // SG_LOG(SG_IO, SG_INFO, "Clearing DDS-TC LRU Cache"); + // filename_hash_cache.clear(); + // SGSceneFeatures::instance()->setReloadCache(false); + //} /* * processor is the interface to the osg_nvtt plugin */ @@ -423,13 +310,21 @@ ModelRegistry::readImage(const string& fileName, if (fileExists(absFileName)) { SGFile f(absFileName); std::string hash; + +// std::string attr = ""; +// if (sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS_NORMALIZED) { +// attr += " effnorm"; +// } +// else if (sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) { +// attr += " eff"; +// // can_compress = false; +// } +// SG_LOG(SG_IO, SG_INFO, absFileName << attr); boost::optional cachehash = filename_hash_cache.get(absFileName); if (cachehash) { hash = *cachehash; - // SG_LOG(SG_IO, SG_ALERT, "Hash for " + absFileName + " in cache " + hash); } else { - // SG_LOG(SG_IO, SG_ALERT, "Creating hash for " + absFileName); try { hash = f.computeHash(); } @@ -444,9 +339,8 @@ ModelRegistry::readImage(const string& fileName, // possibly a shared texture - but warn the user to allow investigation. if (cacheFilename && *cacheFilename != absFileName) { - SG_LOG(SG_IO, SG_ALERT, " Already have " + hash + " : " + *cacheFilename + " not " + absFileName); + SG_LOG(SG_IO, SG_INFO, " Already have " + hash + " : " + *cacheFilename + " not " + absFileName); } - // SG_LOG(SG_IO, SG_ALERT, " >>>> " + hash + " :: " + newName); } newName = cache_root + "/" + hash.substr(0, 2) + "/" + hash + ".cache.dds"; } @@ -456,21 +350,15 @@ ModelRegistry::readImage(const string& fileName, newName += "." + tstream.str(); newName += ".cache.dds"; } - bool doRefresh = refreshCache; - //if (fileExists(newName) && sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) { - // doRefresh = true; - // + //bool doRefresh = refreshCache; + //if (newName != std::string() && fileExists(newName) && doRefresh) { + // if (!filesCleaned.contains(newName)) { + // SG_LOG(SG_IO, SG_INFO, "Removing previously cached effects image " + newName); + // SGPath(newName).remove(); + // filesCleaned.insert(newName, true); + // } //} - if (newName != std::string() && fileExists(newName) && doRefresh) { - if (!filesCleaned.contains(newName)) { - SG_LOG(SG_IO, SG_ALERT, "Removing previously cached effects image " + newName); - SGPath(newName).remove(); - filesCleaned.insert(newName, true); - } - - } - if (newName != std::string() && !fileExists(newName)) { res = registry->readImageImplementation(absFileName, opt); if (res.validImage()) { @@ -494,14 +382,15 @@ ModelRegistry::readImage(const string& fileName, isNormalMap = true; } else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) { - SG_LOG(SG_IO, SG_ALERT, "From effects transparent " + absFileName); + SG_LOG(SG_IO, SG_INFO, "From effects transparent " + absFileName + " will generate mipmap only"); + isEffect = true; + can_compress = false; + } + else if (sgoptC && !transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) { + SG_LOG(SG_IO, SG_INFO, "From effects " + absFileName + " will generate mipmap only"); isEffect = true; // can_compress = false; } - else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) { - SG_LOG(SG_IO, SG_ALERT, "From effects " + absFileName); - isEffect = true; - } if (can_compress) { std::string pot_message; @@ -516,8 +405,9 @@ ModelRegistry::readImage(const string& fileName, resize = true; pot_message += std::string(" not POT: resized height to ") + std::to_string(height); } -if (pot_message.size()) -SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName); + + if (pot_message.size()) + SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName); // unlikely that after resizing in height the width will still be outside of the max texture size. if (height > max_texture_size) @@ -536,6 +426,7 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName); width /= factor; resize = true; } + if (resize) { osg::ref_ptr resizedImage; @@ -601,21 +492,22 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName); // normal maps: // nvdxt.exe - quality_highest - rescaleKaiser - Kaiser - dxt5nm - norm processor->compress(*srcImage, targetFormat, true, true, osgDB::ImageProcessor::USE_CPU, osgDB::ImageProcessor::PRODUCTION); - SG_LOG(SG_IO, SG_ALERT, "-- finished creating DDS: " + newName); + SG_LOG(SG_IO, SG_INFO, "-- finished creating DDS: " + newName); //processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU); } else { simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE); - SG_LOG(SG_IO, SG_WARN, "Texture compression plugin (osg_nvtt) not available; storing uncompressed image: " << absFileName); + SG_LOG(SG_IO, SG_INFO, "Texture compression plugin (osg_nvtt) not available; storing uncompressed image: " << absFileName); srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions); } - } - else { - SG_LOG(SG_IO, SG_ALERT, "Creating uncompressed DDS for " + absFileName); - if (processor) { - processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU); } - else { + else { + SG_LOG(SG_IO, SG_INFO, "Creating uncompressed DDS for " + absFileName); + //if (processor) { + // processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU); + //} + //else + { simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE); srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions); } @@ -660,25 +552,28 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName); osg::ref_ptr srcImage1 = res.getImage(); - //printf(" --> finished loading %s [%s] (%s) %d\n", absFileName.c_str(), srcImage1->getFileName().c_str(), res.loadedFromCache() ? "from cache" : "from disk", res.getImage()->getOrigin()); /* - * Fixup the filename - as when loading from eg. dds.gz the originating filename is lost in the conversion due to the way the OSG loader works - */ - if (srcImage1->getFileName().empty()) { - srcImage1->setFileName(absFileName); - } + * Fixup the filename - as when loading from eg. dds.gz the originating filename is lost in the conversion due to the way the OSG loader works + */ + //if (srcImage1->getFileName().empty()) { + // srcImage1->setFileName(absFileName); + //} srcImage1->setFileName(originalFileName); - if(cache_active && getFileExtension(absFileName) != "dds") + if(cache_active && getFileExtension(absFileName) != "dds"&& getFileExtension(absFileName) != "gz") { - if (processor) { - processor->generateMipMap(*srcImage1, true, osgDB::ImageProcessor::USE_CPU); - SG_LOG(SG_IO, SG_ALERT, "Created nvtt mipmaps DDS for " + absFileName); - } - else { + // In testing the internal mipmap generation works better than the external nvtt one + // (less artefacts); it might be that there are flags we can use to make this better + // but for now we'll just using the built in one + //if (processor) { + // processor->generateMipMap(*srcImage1, true, osgDB::ImageProcessor::USE_CPU); + // SG_LOG(SG_IO, SG_INFO, "Created nvtt mipmaps DDS for " + absFileName); + // } + // else + { simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE); srcImage1 = simgear::effect::computeMipmap(srcImage1, mipmapFunctions); - SG_LOG(SG_IO, SG_ALERT, "Created sg mipmaps DDS for " + absFileName); + SG_LOG(SG_IO, SG_DEBUG, "Created sg mipmaps DDS for " + absFileName); } } @@ -689,75 +584,6 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName); SG_LOG(SG_IO, SG_BULK, "Reading image \"" << res.getImage()->getFileName() << "\""); - // as of March 2018 all patents have expired, https://en.wikipedia.org/wiki/S3_Texture_Compression#Patent - // there is support for S3TC DXT1..5 in MESA https://www.phoronix.com/scan.php?page=news_item&px=S3TC-Lands-In-Mesa - // so it seems that there isn't a valid reason to warn any longer; and beside this is one of those cases where it should - // really only be a developer message -#ifdef WARN_DDS_TEXTURES - // Check for precompressed textures that depend on an extension - switch (res.getImage()->getPixelFormat()) { - - // GL_EXT_texture_compression_s3tc - // patented, no way to decompress these -#ifndef GL_EXT_texture_compression_s3tc -#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 -#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 -#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 -#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 -#endif - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - - // GL_EXT_texture_sRGB - // patented, no way to decompress these -#ifndef GL_EXT_texture_sRGB -#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C -#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D -#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E -#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F -#endif - case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: - - // GL_TDFX_texture_compression_FXT1 - // can decompress these in software but - // no code present in simgear. -#ifndef GL_3DFX_texture_compression_FXT1 -#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 -#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 -#endif - case GL_COMPRESSED_RGB_FXT1_3DFX: - case GL_COMPRESSED_RGBA_FXT1_3DFX: - - // GL_EXT_texture_compression_rgtc - // can decompress these in software but - // no code present in simgear. -#ifndef GL_EXT_texture_compression_rgtc -#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB -#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC -#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD -#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE -#endif - case GL_COMPRESSED_RED_RGTC1_EXT: - case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT: - case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: - case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: - - SG_LOG(SG_IO, SG_WARN, "Image \"" << fileName << "\"\n" - "uses compressed textures which cannot be supported on " - "some systems.\n" - "Please decompress this texture for improved portability."); - break; - - default: - break; - } -#endif - return res; } From 4f6e72de5543cbfdf3ee373e5a0238023ee72b89 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sat, 8 Jun 2019 15:49:15 +0200 Subject: [PATCH 21/39] UDNS Windows compatibility changes For some reason the config file doesn't generate with WINDOWS defined on my system. --- 3rdparty/udns/udns_init.c | 6 +++--- 3rdparty/udns/udns_resolver.c | 8 ++++---- 3rdparty/udns/udns_rr_a.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/3rdparty/udns/udns_init.c b/3rdparty/udns/udns_init.c index 493af589..c28d7c08 100644 --- a/3rdparty/udns/udns_init.c +++ b/3rdparty/udns/udns_init.c @@ -24,7 +24,7 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#ifdef WINDOWS +#if defined(_WINDOWS) || defined(WINDOWS) # include /* includes */ # include /* for dns server addresses etc */ #else @@ -53,7 +53,7 @@ static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) { dns_add_srch(ctx, srch); } -#ifdef WINDOWS +#if defined(_WINDOWS) || defined(WINDOWS) #ifndef NO_IPHLPAPI /* Apparently, some systems does not have proper headers for IPHLPAIP to work. @@ -217,7 +217,7 @@ int dns_init(struct dns_ctx *ctx, int do_open) { ctx = &dns_defctx; dns_reset(ctx); -#ifdef WINDOWS +#if defined(_WINDOWS) || defined(WINDOWS) if (dns_initns_iphlpapi(ctx) != 0) dns_initns_registry(ctx); /*XXX WINDOWS: probably good to get default domain and search list too... diff --git a/3rdparty/udns/udns_resolver.c b/3rdparty/udns/udns_resolver.c index b8f899a2..9f8b7503 100644 --- a/3rdparty/udns/udns_resolver.c +++ b/3rdparty/udns/udns_resolver.c @@ -24,7 +24,7 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#ifdef WINDOWS +#if defined(_WINDOWS) || defined(WINDOWS) # include /* includes */ # include /* needed for struct in6_addr */ #else @@ -392,7 +392,7 @@ dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) { } static unsigned dns_nonrandom_32(void) { -#ifdef WINDOWS +#if defined(_WINDOWS) || defined(WINDOWS) FILETIME ft; GetSystemTimeAsFileTime(&ft); return ft.dwLowDateTime; @@ -551,7 +551,7 @@ int dns_open(struct dns_ctx *ctx) { ctx->dnsc_qstatus = DNS_E_TEMPFAIL; return -1; } -#ifdef WINDOWS +#if defined(_WINDOWS) || defined(WINDOWS) { unsigned long on = 1; if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) { closesocket(sock); @@ -991,7 +991,7 @@ again: /* receive the reply */ * or remote. On local errors, we should stop, while * remote errors should be ignored (for now anyway). */ -#ifdef WINDOWS +#if defined(_WINDOWS) || defined(WINDOWS) if (WSAGetLastError() == WSAEWOULDBLOCK) #else if (errno == EAGAIN) diff --git a/3rdparty/udns/udns_rr_a.c b/3rdparty/udns/udns_rr_a.c index 72cd2022..4492d049 100644 --- a/3rdparty/udns/udns_rr_a.c +++ b/3rdparty/udns/udns_rr_a.c @@ -27,7 +27,7 @@ #include #include #include -#ifndef WINDOWS +#if !defined(_WINDOWS) && !defined(WINDOWS) # include # include #endif From 1ca3f60f8052cb69085e25f0189c4d0ebb63f603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Thu, 6 Jun 2019 23:42:35 +0200 Subject: [PATCH 22/39] Compositor: Significant rework of the clustered shading feature - Added light definitions to the model XML files. This is not compatible with Rembrandt. - Added depth slicing. - Added threading support. For now it's faster to run everything on the main thread since there aren't many lights. --- simgear/scene/material/Effect.cxx | 7 + simgear/scene/material/EffectCullVisitor.cxx | 18 +- simgear/scene/material/EffectCullVisitor.hxx | 7 +- simgear/scene/model/CMakeLists.txt | 4 +- simgear/scene/model/SGLight.cxx | 157 ++++++++ simgear/scene/model/SGLight.hxx | 105 +++++ simgear/scene/model/SGReaderWriterXML.cxx | 9 + simgear/scene/viewer/CMakeLists.txt | 4 +- simgear/scene/viewer/ClusteredForward.cxx | 216 ---------- simgear/scene/viewer/ClusteredForward.hxx | 40 -- simgear/scene/viewer/ClusteredShading.cxx | 395 +++++++++++++++++++ simgear/scene/viewer/ClusteredShading.hxx | 96 +++++ simgear/scene/viewer/CompositorPass.cxx | 35 +- 13 files changed, 824 insertions(+), 269 deletions(-) create mode 100644 simgear/scene/model/SGLight.cxx create mode 100644 simgear/scene/model/SGLight.hxx delete mode 100644 simgear/scene/viewer/ClusteredForward.cxx delete mode 100644 simgear/scene/viewer/ClusteredForward.hxx create mode 100644 simgear/scene/viewer/ClusteredShading.cxx create mode 100644 simgear/scene/viewer/ClusteredShading.hxx diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx index 994adeab..c2240c05 100644 --- a/simgear/scene/material/Effect.cxx +++ b/simgear/scene/material/Effect.cxx @@ -978,6 +978,13 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass, type); program->setParameter(GL_GEOMETRY_OUTPUT_TYPE_EXT, type); } + PropertyList pUniformBlockBindings + = prop->getChildren("uniform-block-binding"); + for (const auto &pUniformBlockBinding : pUniformBlockBindings) { + program->addBindUniformBlock( + pUniformBlockBinding->getStringValue("name"), + pUniformBlockBinding->getIntValue("index")); + } programMap.insert(ProgramMap::value_type(prgKey, program)); resolvedProgramMap.insert(ProgramMap::value_type(resolvedKey, program)); pass->setAttributeAndModes(program); diff --git a/simgear/scene/material/EffectCullVisitor.cxx b/simgear/scene/material/EffectCullVisitor.cxx index 36c31653..b25012a0 100644 --- a/simgear/scene/material/EffectCullVisitor.cxx +++ b/simgear/scene/material/EffectCullVisitor.cxx @@ -21,6 +21,8 @@ #include #include +#include + #include "EffectCullVisitor.hxx" #include "EffectGeode.hxx" @@ -50,6 +52,19 @@ CullVisitor* EffectCullVisitor::clone() const return new EffectCullVisitor(*this); } +void EffectCullVisitor::apply(osg::Node &node) +{ + // TODO: Properly cull lights outside the viewport (override computeBounds()) + // if (isCulled(node)) + // return; + SGLight *light = dynamic_cast(&node); + if (!light) { + CullVisitor::apply(node); + return; + } + _lightList.push_back(light); +} + void EffectCullVisitor::apply(osg::Geode& node) { if (isCulled(node)) @@ -59,9 +74,6 @@ void EffectCullVisitor::apply(osg::Geode& node) CullVisitor::apply(node); return; } - if (_collectLights && ( eg->getNodeMask() & MODELLIGHT_BIT ) ) { - _lightList.push_back( eg ); - } Effect* effect = eg->getEffect(); Technique* technique = 0; if (!effect) { diff --git a/simgear/scene/material/EffectCullVisitor.hxx b/simgear/scene/material/EffectCullVisitor.hxx index 340ce7c7..3669126b 100644 --- a/simgear/scene/material/EffectCullVisitor.hxx +++ b/simgear/scene/material/EffectCullVisitor.hxx @@ -21,6 +21,8 @@ #include +#include + namespace osg { class Geode; @@ -38,6 +40,7 @@ public: EffectCullVisitor(const EffectCullVisitor&); virtual osgUtil::CullVisitor* clone() const; using osgUtil::CullVisitor::apply; + virtual void apply(osg::Node& node); virtual void apply(osg::Geode& node); virtual void reset(); @@ -45,9 +48,11 @@ public: void addBuffer(std::string b, osg::Texture2D* tex); osg::Texture2D* getBuffer(std::string b); + SGLightList getLightList() const { return _lightList; } + private: std::map > _bufferList; - std::vector > _lightList; + SGLightList _lightList; bool _collectLights; std::string _effScheme; }; diff --git a/simgear/scene/model/CMakeLists.txt b/simgear/scene/model/CMakeLists.txt index 69bb545f..50227941 100644 --- a/simgear/scene/model/CMakeLists.txt +++ b/simgear/scene/model/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADERS PrimitiveCollector.hxx SGClipGroup.hxx SGInteractionAnimation.hxx + SGLight.hxx SGMaterialAnimation.hxx SGPickAnimation.hxx SGOffsetTransform.hxx @@ -35,6 +36,7 @@ set(SOURCES PrimitiveCollector.cxx SGClipGroup.cxx SGInteractionAnimation.cxx + SGLight.cxx SGLightAnimation.cxx SGPickAnimation.cxx SGMaterialAnimation.cxx @@ -62,4 +64,4 @@ if(ENABLE_TESTS) target_link_libraries(test_animations ${TEST_LIBS} ${OPENSCENEGRAPH_LIBRARIES}) add_test(animations ${EXECUTABLE_OUTPUT_PATH}/test_animations) -endif(ENABLE_TESTS) \ No newline at end of file +endif(ENABLE_TESTS) diff --git a/simgear/scene/model/SGLight.cxx b/simgear/scene/model/SGLight.cxx new file mode 100644 index 00000000..ab332750 --- /dev/null +++ b/simgear/scene/model/SGLight.cxx @@ -0,0 +1,157 @@ +// Copyright (C) 2018 Fernando García Liñán +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#include "SGLight.hxx" + +#include +#include +#include +#include +#include + +#include +#include + +class SGLightDebugListener : public SGPropertyChangeListener { +public: + SGLightDebugListener(osg::Switch *sw) : _sw(sw) {} + virtual void valueChanged(SGPropertyNode *node) { + _sw->setValue(0, node->getBoolValue()); + } +private: + osg::ref_ptr _sw; +}; + +osg::Node * +SGLight::appendLight(const SGPropertyNode *configNode, + SGPropertyNode *modelRoot, + const osgDB::Options *options) +{ + SGConstPropertyNode_ptr p; + + SGLight *light = new SGLight; + + if((p = configNode->getNode("type")) != NULL) { + std::string type = p->getStringValue(); + if (type == "point") + light->setType(SGLight::Type::POINT); + else if (type == "spot") + light->setType(SGLight::Type::SPOT); + else + SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown light type '" << type << "'"); + } + + light->setRange(configNode->getFloatValue("range-m")); + +#define SGLIGHT_GET_COLOR_VALUE(n) \ + osg::Vec4(configNode->getFloatValue(n "/r"), \ + configNode->getFloatValue(n "/g"), \ + configNode->getFloatValue(n "/b"), \ + configNode->getFloatValue(n "/a")) + light->setAmbient(SGLIGHT_GET_COLOR_VALUE("ambient")); + light->setDiffuse(SGLIGHT_GET_COLOR_VALUE("diffuse")); + light->setSpecular(SGLIGHT_GET_COLOR_VALUE("specular")); +#undef SGLIGHT_GET_COLOR_VALUE + + light->setConstantAttenuation(configNode->getFloatValue("attenuation/c")); + light->setLinearAttenuation(configNode->getFloatValue("attenuation/l")); + light->setQuadraticAttenuation(configNode->getFloatValue("attenuation/q")); + + light->setSpotExponent(configNode->getFloatValue("spot-exponent")); + light->setSpotCutoff(configNode->getFloatValue("spot-cutoff")); + + osg::Group *group = 0; + if ((p = configNode->getNode("offsets")) == NULL) { + group = new osg::Group; + } else { + // Set up the alignment node ("stolen" from animation.cxx) + // XXX Order of rotations is probably not correct. + osg::MatrixTransform *align = new osg::MatrixTransform; + osg::Matrix res_matrix; + res_matrix.makeRotate( + p->getFloatValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, + osg::Vec3(0, 1, 0), + p->getFloatValue("roll-deg", 0.0)*SG_DEGREES_TO_RADIANS, + osg::Vec3(1, 0, 0), + p->getFloatValue("heading-deg", 0.0)*SG_DEGREES_TO_RADIANS, + osg::Vec3(0, 0, 1)); + + osg::Matrix tmat; + tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0), + configNode->getFloatValue("offsets/y-m", 0.0), + configNode->getFloatValue("offsets/z-m", 0.0)); + + align->setMatrix(res_matrix * tmat); + group = align; + } + + group->addChild(light); + + osg::Shape *debug_shape; + if (light->getType() == SGLight::Type::POINT) { + debug_shape = new osg::Sphere(osg::Vec3(0, 0, 0), light->getRange()); + } else if (light->getType() == SGLight::Type::SPOT) { + debug_shape = new osg::Cone( + // Origin of the cone is at its center of mass + osg::Vec3(0, 0, -0.75 * light->getRange()), + tan(light->getSpotCutoff() * SG_DEGREES_TO_RADIANS) * light->getRange(), + light->getRange()); + } + osg::ShapeDrawable *debug_drawable = new osg::ShapeDrawable(debug_shape); + debug_drawable->setColor(osg::Vec4(1.0, 0.0, 0.0, 1.0)); + osg::Geode *debug_geode = new osg::Geode; + debug_geode->addDrawable(debug_drawable); + + osg::StateSet *debug_ss = debug_drawable->getOrCreateStateSet(); + debug_ss->setAttributeAndModes( + new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE), + osg::StateAttribute::ON); + debug_ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + osg::Switch *debug_switch = new osg::Switch; + debug_switch->addChild(debug_geode); + simgear::getPropertyRoot()->getNode("/sim/debug/show-light-volumes", true)-> + addChangeListener(new SGLightDebugListener(debug_switch), true); + group->addChild(debug_switch); + + if ((p = configNode->getNode("name")) != NULL) + group->setName(p->getStringValue()); + else + group->setName("light"); + + return group; +} + +SGLight::SGLight() : + _type(Type::POINT), + _range(0.0f) +{ + // Default values taken from osg::Light + // They don't matter anyway as they are overwritten by the XML config values + _ambient.set(0.05f, 0.05f, 0.05f, 1.0f); + _diffuse.set(0.8f, 0.8f, 0.8f, 1.0f); + _specular.set(0.05f, 0.05f, 0.05f, 1.0f); + _constant_attenuation = 1.0f; + _linear_attenuation = 0.0f; + _quadratic_attenuation = 0.0f; + _spot_exponent = 0.0f; + _spot_cutoff = 180.0f; +} + +SGLight::~SGLight() +{ + +} diff --git a/simgear/scene/model/SGLight.hxx b/simgear/scene/model/SGLight.hxx new file mode 100644 index 00000000..2a25413b --- /dev/null +++ b/simgear/scene/model/SGLight.hxx @@ -0,0 +1,105 @@ +// Copyright (C) 2018 Fernando García Liñán +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SG_LIGHT_HXX +#define SG_LIGHT_HXX + +#include +#include + +#include + +class SGLight : public osg::Node { +public: + enum Type { + POINT, + SPOT + }; + + SGLight(); + + SGLight(const SGLight& l, + const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) : + osg::Node(l, copyop), + _type(l._type), + _range(l._range), + _ambient(l._ambient), + _diffuse(l._diffuse), + _specular(l._specular), + _constant_attenuation(l._constant_attenuation), + _linear_attenuation(l._linear_attenuation), + _quadratic_attenuation(l._quadratic_attenuation), + _spot_exponent(l._spot_exponent), + _spot_cutoff(l._spot_cutoff) + {} + + META_Node(simgear, SGLight); + + static osg::Node *appendLight(const SGPropertyNode *configNode, + SGPropertyNode *modelRoot, + const osgDB::Options *options); + + void setType(Type type) { _type = type; } + Type getType() const { return _type; } + + void setRange(float range) { _range = range; } + float getRange() const { return _range; } + + void setAmbient(const osg::Vec4 &ambient) { _ambient = ambient; } + const osg::Vec4 &getAmbient() const { return _ambient; } + + void setDiffuse(const osg::Vec4 &diffuse) { _diffuse = diffuse; } + const osg::Vec4 &getDiffuse() const { return _diffuse; } + + void setSpecular(const osg::Vec4 &specular) { _specular = specular; } + const osg::Vec4 &getSpecular() const { return _specular; } + + void setConstantAttenuation(float constant_attenuation) { _constant_attenuation = constant_attenuation; } + float getConstantAttenuation() const { return _constant_attenuation; } + + void setLinearAttenuation(float linear_attenuation) { _linear_attenuation = linear_attenuation; } + float getLinearAttenuation() const { return _linear_attenuation; } + + void setQuadraticAttenuation(float quadratic_attenuation) { _quadratic_attenuation = quadratic_attenuation; } + float getQuadraticAttenuation() const { return _quadratic_attenuation; } + + void setSpotExponent(float spot_exponent) { _spot_exponent = spot_exponent; } + float getSpotExponent() const { return _spot_exponent; } + + void setSpotCutoff(float spot_cutoff) { _spot_cutoff = spot_cutoff; } + float getSpotCutoff() const { return _spot_cutoff; } + +protected: + virtual ~SGLight(); + + Type _type; + + float _range; + + osg::Vec4 _ambient; + osg::Vec4 _diffuse; + osg::Vec4 _specular; + + float _constant_attenuation; + float _linear_attenuation; + float _quadratic_attenuation; + float _spot_exponent; + float _spot_cutoff; +}; + +typedef std::vector> SGLightList; + +#endif /* SG_LIGHT_HXX */ diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index c09a9579..b6fc37e9 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -50,6 +50,7 @@ #include "animation.hxx" #include "particles.hxx" #include "model.hxx" +#include "SGLight.hxx" #include "SGText.hxx" #include "SGMaterialAnimation.hxx" @@ -518,6 +519,14 @@ sgLoad3DModel_internal(const SGPath& path, options.get())); } + std::vector light_nodes; + light_nodes = props->getChildren("light"); + for (unsigned i = 0; i < light_nodes.size(); ++i) { + group->addChild(SGLight::appendLight(light_nodes[i], + prop_root, + options.get())); + } + PropertyList effect_nodes = props->getChildren("effect"); PropertyList animation_nodes = props->getChildren("animation"); diff --git a/simgear/scene/viewer/CMakeLists.txt b/simgear/scene/viewer/CMakeLists.txt index d26297fc..ef223b56 100644 --- a/simgear/scene/viewer/CMakeLists.txt +++ b/simgear/scene/viewer/CMakeLists.txt @@ -1,5 +1,5 @@ set(HEADERS - ClusteredForward.hxx + ClusteredShading.hxx Compositor.hxx CompositorBuffer.hxx CompositorPass.hxx @@ -7,7 +7,7 @@ set(HEADERS ) set(SOURCES - ClusteredForward.cxx + ClusteredShading.cxx Compositor.cxx CompositorBuffer.cxx CompositorPass.cxx diff --git a/simgear/scene/viewer/ClusteredForward.cxx b/simgear/scene/viewer/ClusteredForward.cxx deleted file mode 100644 index e2c9d593..00000000 --- a/simgear/scene/viewer/ClusteredForward.cxx +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (C) 2018 Fernando García Liñán -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Library General Public -// License as published by the Free Software Foundation; either -// version 2 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - -#include "ClusteredForward.hxx" - -#include -#include -#include -#include -#include -#include - -namespace simgear { -namespace compositor { - -///// BEGIN DEBUG -#define DATA_SIZE 24 -const GLfloat LIGHT_DATA[DATA_SIZE] = { - 0.0, 0.0, -10.0, 1.0, 1.0, 0.0, 0.0, 1.0, - 0.0, 0.0, 10.0, 1.0, 0.0, 1.0, 0.0, 1.0, - 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0 -}; - -#define MAX_LIGHT_INDICES 4096 -#define MAX_POINT_LIGHTS 256 - -struct Light { - osg::Vec3 position; - float range; -}; - -#define NUM_LIGHTS 2 -Light LIGHT_LIST[NUM_LIGHTS] = { - {osg::Vec3(0.0f, 0.0f, -10.0f), 10.0f}, - {osg::Vec3(0.0f, 0.0f, 5.0f), 1000.0f} -}; -///// END DEBUG - -ClusteredForwardDrawCallback::ClusteredForwardDrawCallback(int tile_size) : - _initialized(false), - _tile_size(tile_size), - _light_grid(new osg::Image), - _light_indices(new osg::Image), - _light_data(new osg::FloatArray(MAX_POINT_LIGHTS)) -{ -} - -void -ClusteredForwardDrawCallback::operator()(osg::RenderInfo &renderInfo) const -{ - osg::Camera *camera = renderInfo.getCurrentCamera(); - const osg::Viewport *vp = camera->getViewport(); - const int width = vp->width(); - const int height = vp->height(); - - // Round up - int n_htiles = (width + _tile_size - 1) / _tile_size; - int n_vtiles = (height + _tile_size - 1) / _tile_size; - - if (!_initialized) { - // Create and associate the light grid 3D texture - _light_grid->allocateImage(n_htiles, n_vtiles, 1, - GL_RGB_INTEGER_EXT, GL_UNSIGNED_SHORT); - _light_grid->setInternalTextureFormat(GL_RGB16UI_EXT); - - osg::ref_ptr light_grid_tex = new osg::Texture3D; - light_grid_tex->setResizeNonPowerOfTwoHint(false); - light_grid_tex->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER); - light_grid_tex->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER); - light_grid_tex->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER); - light_grid_tex->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::NEAREST); - light_grid_tex->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::NEAREST); - light_grid_tex->setImage(0, _light_grid.get()); - - camera->getOrCreateStateSet()->setTextureAttributeAndModes( - 10, light_grid_tex.get(), osg::StateAttribute::ON); - - // Create and associate the light indices TBO - _light_indices->allocateImage(4096, 1, 1, GL_RED_INTEGER_EXT, GL_UNSIGNED_SHORT); - - osg::ref_ptr light_indices_tbo = - new osg::TextureBuffer; - light_indices_tbo->setInternalFormat(GL_R16UI); - light_indices_tbo->setImage(_light_indices.get()); - - camera->getOrCreateStateSet()->setTextureAttribute( - 11, light_indices_tbo.get()); - - // Create and associate the light data UBO - osg::ref_ptr light_data_ubo = - new osg::UniformBufferObject; - _light_data->setBufferObject(light_data_ubo.get()); - -#if OSG_VERSION_LESS_THAN(3,6,0) - osg::ref_ptr light_data_ubb = - new osg::UniformBufferBinding(0, light_data_ubo.get(), - 0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat)); -#else - osg::ref_ptr light_data_ubb = - new osg::UniformBufferBinding(0, _light_data.get(), - 0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat)); -#endif - light_data_ubb->setDataVariance(osg::Object::DYNAMIC); - - camera->getOrCreateStateSet()->setAttribute( - light_data_ubb.get(), osg::StateAttribute::ON); - - _initialized = true; - } - - std::vector subfrustums; - const osg::Matrix &view_matrix = camera->getViewMatrix(); - const osg::Matrix &proj_matrix = camera->getProjectionMatrix(); - osg::Matrix view_proj_inverse = osg::Matrix::inverse(view_matrix * proj_matrix); - - double x_step = (_tile_size / width) * 2.0; - double y_step = (_tile_size / height) * 2.0; - for (int y = 0; y < n_vtiles; ++y) { - for (int x = 0; x < n_htiles; ++x) { - // Create the subfrustum in clip space - double x_min = -1.0 + x_step * x; double x_max = x_min + x_step; - double y_min = -1.0 + y_step * y; double y_max = y_min + y_step; - double z_min = 1.0; double z_max = -1.0; - osg::BoundingBox subfrustum_bb( - x_min, y_min, z_min, x_max, y_max, z_max); - osg::Polytope subfrustum; - subfrustum.setToBoundingBox(subfrustum_bb); - - // Transform it to world space - subfrustum.transformProvidingInverse(view_proj_inverse); - - subfrustums.push_back(subfrustum); - } - } - - GLushort *grid_data = reinterpret_cast - (_light_grid->data()); - GLushort *index_data = reinterpret_cast - (_light_indices->data()); - - GLushort global_light_count = 0; - for (size_t i = 0; i < subfrustums.size(); ++i) { - GLushort start_offset = global_light_count; - GLushort local_light_count = 0; - - for (GLushort light_list_index = 0; - light_list_index < NUM_LIGHTS; - ++light_list_index) { - const Light &light = LIGHT_LIST[light_list_index]; - osg::BoundingSphere bs(light.position, light.range); - - if (subfrustums[i].contains(bs)) { - index_data[global_light_count] = light_list_index; - ++local_light_count; - ++global_light_count; - } - } - grid_data[i * 3 + 0] = start_offset; - grid_data[i * 3 + 1] = local_light_count; - grid_data[i * 3 + 2] = 0; - } - - _light_grid->dirty(); - _light_indices->dirty(); - - // Upload light data - for (int i = 0; i < DATA_SIZE; ++i) { - (*_light_data)[i] = LIGHT_DATA[i]; - } - - // DEBUG - /* - if (!_debug) { - for (int y = 0; y < num_vtiles; ++y) { - for (int x = 0; x < num_htiles; ++x) { - std::cout << grid_data[(y * num_htiles + x) * 3 + 0] << "," - << grid_data[(y * num_htiles + x) * 3 + 1] << " "; - } - std::cout << std::endl; - } - std::cout << "\n\n"; - - for (int i = 0; i < num_vtiles * num_htiles; ++i) { - std::cout << index_data[i] << " "; - } - std::cout << "\n"; - _debug = true; - } - */ -/* - for (int y = 0; y < num_vtiles; ++y) { - for (int x = 0; x < num_htiles; ++x) { - data[(y * num_htiles + x) * 3 + 0] = (unsigned short)x; - data[(y * num_htiles + x) * 3 + 1] = (unsigned short)y; - data[(y * num_htiles + x) * 3 + 2] = 0; - } - } - _light_grid->dirty(); -*/ -} - -} // namespace compositor -} // namespace simgear diff --git a/simgear/scene/viewer/ClusteredForward.hxx b/simgear/scene/viewer/ClusteredForward.hxx deleted file mode 100644 index 63da3af6..00000000 --- a/simgear/scene/viewer/ClusteredForward.hxx +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2018 Fernando García Liñán -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Library General Public -// License as published by the Free Software Foundation; either -// version 2 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - -#ifndef SG_CLUSTERED_FORWARD_HXX -#define SG_CLUSTERED_FORWARD_HXX - -#include - -namespace simgear { -namespace compositor { - -class ClusteredForwardDrawCallback : public osg::Camera::DrawCallback { -public: - ClusteredForwardDrawCallback(int tile_size); - virtual void operator()(osg::RenderInfo &renderInfo) const; -protected: - mutable bool _initialized; - int _tile_size; - osg::ref_ptr _light_grid; - osg::ref_ptr _light_indices; - osg::ref_ptr _light_data; -}; - -} // namespace compositor -} // namespace simgear - -#endif /* SG_CLUSTERED_FORWARD_HXX */ diff --git a/simgear/scene/viewer/ClusteredShading.cxx b/simgear/scene/viewer/ClusteredShading.cxx new file mode 100644 index 00000000..dcd13720 --- /dev/null +++ b/simgear/scene/viewer/ClusteredShading.cxx @@ -0,0 +1,395 @@ +// Copyright (C) 2018 Fernando García Liñán +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#include "ClusteredShading.hxx" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace simgear { +namespace compositor { + +const int MAX_LIGHT_INDICES = 524288; // 1 MB (2 bytes per index) +const int MAX_POINTLIGHTS = 256; +const int MAX_SPOTLIGHTS = 256; + +// Size in floats (4 bytes) of the light struct to be passed to the GLSL shader. +// It must be a multiple of the size of a vec4 as per the std140 layout rules. +// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt +const int POINTLIGHT_BLOCK_SIZE = 20; +const int SPOTLIGHT_BLOCK_SIZE = 8; + +ClusteredShading::ClusteredShading(osg::Camera *camera, + const SGPropertyNode *config) : + _camera(camera) +{ + _tile_size = config->getIntValue("tile-size", 128); + _depth_slices = config->getIntValue("depth-slices", 1); + _num_threads = config->getIntValue("num-threads", 1); + _slices_per_thread = _depth_slices / _num_threads; + if (_slices_per_thread == 0) { + SG_LOG(SG_INPUT, SG_INFO, "ClusteredShading::ClusteredShading(): " + "More threads than depth slices"); + _num_threads = _depth_slices; + } + _slices_remainder = _depth_slices % _num_threads; + + osg::StateSet *ss = _camera->getOrCreateStateSet(); + + osg::Uniform *tile_size_uniform = + new osg::Uniform("fg_ClusteredTileSize", _tile_size); + ss->addUniform(tile_size_uniform); + _slice_scale = new osg::Uniform("fg_ClusteredSliceScale", 0.0f); + ss->addUniform(_slice_scale.get()); + _slice_bias = new osg::Uniform("fg_ClusteredSliceBias", 0.0f); + ss->addUniform(_slice_bias.get()); + + // Create and associate the light grid 3D texture + //////////////////////////////////////////////////////////////////////////// + _light_grid = new osg::Image; + _light_grid->setInternalTextureFormat(GL_RGB32UI_EXT); + // Image allocation happens in setupSubfrusta() because the light grid size + // can change at runtime (viewport resize) + + osg::ref_ptr light_grid_tex = new osg::Texture3D; + light_grid_tex->setResizeNonPowerOfTwoHint(false); + light_grid_tex->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER); + light_grid_tex->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER); + light_grid_tex->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER); + light_grid_tex->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::NEAREST); + light_grid_tex->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::NEAREST); + light_grid_tex->setImage(_light_grid.get()); + + int light_grid_bind_unit = config->getIntValue("grid-bind-unit", 11); + ss->setTextureAttributeAndModes( + light_grid_bind_unit, light_grid_tex.get(), osg::StateAttribute::ON); + + osg::ref_ptr light_grid_uniform = + new osg::Uniform("fg_ClusteredLightGrid", light_grid_bind_unit); + ss->addUniform(light_grid_uniform.get()); + + // Create and associate the light indices TBO + //////////////////////////////////////////////////////////////////////////// + _light_indices = new osg::Image; + _light_indices->allocateImage( + MAX_LIGHT_INDICES, 1, 1, GL_RED_INTEGER_EXT, GL_UNSIGNED_SHORT); + + osg::ref_ptr light_indices_tbo = + new osg::TextureBuffer; + light_indices_tbo->setInternalFormat(GL_R16UI); + light_indices_tbo->setImage(_light_indices.get()); + + int light_indices_bind_unit = config->getIntValue("indices-bind-unit", 12); + ss->setTextureAttribute(light_indices_bind_unit, light_indices_tbo.get()); + + osg::ref_ptr light_indices_uniform = + new osg::Uniform("fg_ClusteredLightIndices", light_indices_bind_unit); + ss->addUniform(light_indices_uniform.get()); + + // Create and associate the pointlight data UBO + //////////////////////////////////////////////////////////////////////////// + _pointlight_data = new osg::FloatArray(MAX_POINTLIGHTS * POINTLIGHT_BLOCK_SIZE); + + osg::ref_ptr pointlight_data_ubo = + new osg::UniformBufferObject; + _pointlight_data->setBufferObject(pointlight_data_ubo.get()); + + int pointlight_ubo_index = config->getIntValue("pointlight-ubo-index", 5); +#if OSG_VERSION_LESS_THAN(3,6,0) + osg::ref_ptr pointlight_data_ubb = + new osg::UniformBufferBinding( + pointlight_ubo_index, + pointlight_data_ubo.get(), + 0, + MAX_POINTLIGHTS * POINTLIGHT_BLOCK_SIZE * sizeof(GLfloat)); +#else + osg::ref_ptr pointlight_data_ubb = + new osg::UniformBufferBinding( + pointlight_ubo_index, + _pointlight_data.get(), + 0, + MAX_POINTLIGHTS * POINTLIGHT_BLOCK_SIZE * sizeof(GLfloat)); +#endif + pointlight_data_ubb->setDataVariance(osg::Object::DYNAMIC); + ss->setAttribute(pointlight_data_ubb.get(), osg::StateAttribute::ON); + + // Create and associate the spotlight data UBO + //////////////////////////////////////////////////////////////////////////// + _spotlight_data = new osg::FloatArray(MAX_SPOTLIGHTS * SPOTLIGHT_BLOCK_SIZE); + + osg::ref_ptr spotlight_data_ubo = + new osg::UniformBufferObject; + _spotlight_data->setBufferObject(spotlight_data_ubo.get()); + + int spotlight_ubo_index = config->getIntValue("spotlight-ubo-index", 6); +#if OSG_VERSION_LESS_THAN(3,6,0) + osg::ref_ptr spotlight_data_ubb = + new osg::UniformBufferBinding( + spotlight_ubo_index, + spotlight_data_ubo.get(), + 0, + MAX_SPOTLIGHTS * SPOTLIGHT_BLOCK_SIZE * sizeof(GLfloat)); +#else + osg::ref_ptr spotlight_data_ubb = + new osg::UniformBufferBinding( + spotlight_ubo_index, + _spotlight_data.get(), + 0, + MAX_SPOTLIGHTS * SPOTLIGHT_BLOCK_SIZE * sizeof(GLfloat)); +#endif + spotlight_data_ubb->setDataVariance(osg::Object::DYNAMIC); + ss->setAttribute(spotlight_data_ubb.get(), osg::StateAttribute::ON); +} + +ClusteredShading::~ClusteredShading() +{ +} + +void +ClusteredShading::update(const SGLightList &light_list) +{ + // Transform every light to a more comfortable data structure for collision + // testing, separating point and spot lights in the process + _point_bounds.clear(); + _spot_bounds.clear(); + for (const auto &light : light_list) { + if (light->getType() == SGLight::Type::POINT) { + PointlightBound point; + point.light = light; + point.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) * + osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * + _camera->getViewMatrix(); + point.range = light->getRange(); + _point_bounds.push_back(point); + } else if (light->getType() == SGLight::Type::SPOT) { + + } + } + if (_point_bounds.size() > MAX_POINTLIGHTS || + _spot_bounds.size() > MAX_SPOTLIGHTS) { + throw sg_range_exception("Maximum amount of visible lights surpassed"); + } + + float l, r, b, t; + _camera->getProjectionMatrix().getFrustum(l, r, b, t, _zNear, _zFar); + _slice_scale->set(_depth_slices / log2(_zFar / _zNear)); + _slice_bias->set(-_depth_slices * log2(_zNear) / log2(_zFar / _zNear)); + + const osg::Viewport *vp = _camera->getViewport(); + static int old_width = 0, old_height = 0; + int width = vp->width(); int height = vp->height(); + if (width != old_width || height != old_height) { + old_width = width; old_height = height; + + _n_htiles = (width + _tile_size - 1) / _tile_size; + _n_vtiles = (height + _tile_size - 1) / _tile_size; + + _x_step = (_tile_size / float(width)) * 2.0; + _y_step = (_tile_size / float(height)) * 2.0; + + _light_grid->allocateImage(_n_htiles, _n_vtiles, _depth_slices, + GL_RGB_INTEGER_EXT, GL_UNSIGNED_INT); + _subfrusta.reset(new Subfrustum[_n_htiles * _n_vtiles]); + } + + for (int y = 0; y < _n_vtiles; ++y) { + float ymin = -1.0 + _y_step * float(y); + float ymax = ymin + _y_step; + for (int x = 0; x < _n_htiles; ++x) { + float xmin = -1.0 + _x_step * float(x); + float xmax = xmin + _x_step; + + // Create the subfrustum in clip space + // The near and far planes will be filled later as they change from + // slice to slice + Subfrustum &subfrustum = _subfrusta[y*_n_htiles + x]; + subfrustum.plane[0].set(1.0f,0.0f,0.0f,-xmin); // left plane. + subfrustum.plane[1].set(-1.0f,0.0f,0.0f,xmax); // right plane. + subfrustum.plane[2].set(0.0f,1.0f,0.0f,-ymin); // bottom plane. + subfrustum.plane[3].set(0.0f,-1.0f,0.0f,ymax); // top plane. + + // Transform it to view space + for (int i = 0; i < 4; ++i) { + osg::Vec4f &p = subfrustum.plane[i]; + p = _camera->getProjectionMatrix() * p; + float inv_length = 1.0 / sqrt(p._v[0]*p._v[0] + + p._v[1]*p._v[1] + + p._v[2]*p._v[2]); + p *= inv_length; + } + } + } + + _global_light_count = 0; + + if (_depth_slices == 1) { + // Just run the light assignment on the main thread to avoid the + // unnecessary threading overhead + assignLightsToSlice(0); + } else if (_num_threads == 1) { + // Again, avoid the unnecessary threading overhead + threadFunc(0); + } else { + std::thread light_threads[_num_threads]; + for (int i = 0; i < _num_threads; ++i) + light_threads[i] = std::thread(&ClusteredShading::threadFunc, this, i); + + for (int i = 0; i < _num_threads; ++i) + light_threads[i].join(); + } + + // Force upload of the image data + _light_grid->dirty(); + _light_indices->dirty(); + + // Upload pointlight data + writePointlightData(); +} + +void +ClusteredShading::threadFunc(int thread_id) +{ + for (int i = 0; i < _slices_per_thread; ++i) + assignLightsToSlice(thread_id * _slices_per_thread + i); + + if (_slices_remainder > thread_id) + assignLightsToSlice(_slices_per_thread * _num_threads + thread_id); +} + +void +ClusteredShading::assignLightsToSlice(int slice) +{ + size_t z_offset = slice * _n_htiles * _n_vtiles; + + float near = getDepthForSlice(slice); + float far = getDepthForSlice(slice + 1); + osg::Vec4f near_plane(0.0f, 0.0f, -1.0f, -near); + osg::Vec4f far_plane (0.0f, 0.0f, 1.0f, far); + + GLuint *grid = reinterpret_cast(_light_grid->data()); + GLushort *indices = reinterpret_cast(_light_indices->data()); + + for (int i = 0; i < (_n_htiles * _n_vtiles); ++i) { + Subfrustum subfrustum = _subfrusta[i]; + subfrustum.plane[4] = near_plane; + subfrustum.plane[5] = far_plane; + + GLuint start_offset = _global_light_count; + GLuint local_point_count = 0; + GLuint local_spot_count = 0; + + for (GLushort point_iterator = 0; + point_iterator < _point_bounds.size(); + ++point_iterator) { + PointlightBound point = _point_bounds[point_iterator]; + + // Perform frustum-sphere collision tests + float distance = 0.0f; + for (int j = 0; j < 6; j++) { + distance = subfrustum.plane[j] * point.position + point.range; + if (distance <= 0.0f) + break; + } + + if (distance > 0.0f) { + // Update light index list + indices[_global_light_count] = point_iterator; + ++local_point_count; + ++_global_light_count; // Atomic increment + } + + if (_global_light_count >= MAX_LIGHT_INDICES) { + throw sg_range_exception( + "Clustered shading light index count is over the hardcoded limit (" + + std::to_string(MAX_LIGHT_INDICES) + ")"); + } + } + + // Update light grid + grid[(z_offset + i) * 3 + 0] = start_offset; + grid[(z_offset + i) * 3 + 1] = local_point_count; + grid[(z_offset + i) * 3 + 2] = local_spot_count; + } + + // for (int y = 0; y < _n_vtiles; ++y) { + // for (int x = 0; x < _n_htiles; ++x) { + // std::cout << grid[(y * _n_htiles + x) * 3 + 0] << "," + // << grid[(y * _n_htiles + x) * 3 + 1] << " "; + // } + // std::cout << std::endl; + // } + // std::cout << "\n\n"; + + // for (int i = 0; i < n_vtiles * n_htiles; ++i) { + // std::cout << indices[i] << " "; + // } + // std::cout << "\n"; +} + +void +ClusteredShading::writePointlightData() +{ + GLfloat *data = reinterpret_cast(&(*_pointlight_data)[0]); + + for (const auto &point : _point_bounds) { + // vec4 position + *data++ = point.position.x(); + *data++ = point.position.y(); + *data++ = point.position.z(); + *data++ = 1.0f; + // vec4 ambient + *data++ = point.light->getAmbient().x(); + *data++ = point.light->getAmbient().y(); + *data++ = point.light->getAmbient().z(); + *data++ = point.light->getAmbient().w(); + // vec4 diffuse + *data++ = point.light->getDiffuse().x(); + *data++ = point.light->getDiffuse().y(); + *data++ = point.light->getDiffuse().z(); + *data++ = point.light->getDiffuse().w(); + // vec4 specular + *data++ = point.light->getSpecular().x(); + *data++ = point.light->getSpecular().y(); + *data++ = point.light->getSpecular().z(); + *data++ = point.light->getSpecular().w(); + // vec4 attenuation (x = constant, y = linear, z = quadratic, w = range) + *data++ = point.light->getConstantAttenuation(); + *data++ = point.light->getLinearAttenuation(); + *data++ = point.light->getQuadraticAttenuation(); + *data++ = point.light->getRange(); + // No padding needed as the resulting size is a multiple of vec4 + } + _pointlight_data->dirty(); +} + +float +ClusteredShading::getDepthForSlice(int slice) const +{ + return _zNear * pow(_zFar / _zNear, float(slice) / _depth_slices); +} + +} // namespace compositor +} // namespace simgear diff --git a/simgear/scene/viewer/ClusteredShading.hxx b/simgear/scene/viewer/ClusteredShading.hxx new file mode 100644 index 00000000..4843a102 --- /dev/null +++ b/simgear/scene/viewer/ClusteredShading.hxx @@ -0,0 +1,96 @@ +// Copyright (C) 2018 Fernando García Liñán +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SG_CLUSTERED_SHADING_HXX +#define SG_CLUSTERED_SHADING_HXX + +#include + +#include +#include + +#include + +namespace simgear { +namespace compositor { + +class ClusteredShading : public osg::Referenced { +public: + ClusteredShading(osg::Camera *camera, const SGPropertyNode *config); + ~ClusteredShading(); + + void update(const SGLightList &light_list); +protected: + // We could make use of osg::Polytope, but it does a lot of std::vector + // push_back() calls, so we make our own frustum structure for huge + // performance gains. + struct Subfrustum { + osg::Vec4f plane[6]; + }; + + struct PointlightBound { + SGLight *light; + osg::Vec4f position; + float range; + }; + struct SpotlightBound { + SGLight *light; + osg::Vec4f position; + float range; + }; + + void threadFunc(int thread_id); + void assignLightsToSlice(int slice); + void writePointlightData(); + float getDepthForSlice(int slice) const; + + osg::observer_ptr _camera; + + osg::ref_ptr _slice_scale; + osg::ref_ptr _slice_bias; + + int _tile_size; + int _depth_slices; + int _num_threads; + int _slices_per_thread; + int _slices_remainder; + + float _zNear; + float _zFar; + + int _n_htiles; + int _n_vtiles; + + float _x_step; + float _y_step; + + osg::ref_ptr _light_grid; + osg::ref_ptr _light_indices; + osg::ref_ptr _pointlight_data; + osg::ref_ptr _spotlight_data; + + std::unique_ptr _subfrusta; + + std::vector _point_bounds; + std::vector _spot_bounds; + + std::atomic _global_light_count; +}; + +} // namespace compositor +} // namespace simgear + +#endif /* SG_CLUSTERED_SHADING_HXX */ diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index 939cc7b6..7e82765d 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -25,13 +25,14 @@ #include #include +#include #include #include #include #include #include -#include "ClusteredForward.hxx" +#include "ClusteredShading.hxx" #include "Compositor.hxx" #include "CompositorUtil.hxx" @@ -656,6 +657,27 @@ protected: float _zFar; }; +class SceneCullCallback : public osg::NodeCallback { +public: + SceneCullCallback(ClusteredShading *clustered) : + _clustered(clustered) {} + + virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) { + osg::Camera *camera = static_cast(node); + EffectCullVisitor *cv = dynamic_cast(nv); + + cv->traverse(*camera); + + if (_clustered) { + // Retrieve the light list from the cull visitor + SGLightList light_list = cv->getLightList(); + _clustered->update(light_list); + } + } +protected: + osg::ref_ptr _clustered; +}; + struct ScenePassBuilder : public PassBuilder { public: virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, @@ -666,11 +688,12 @@ public: osg::Camera *camera = pass->camera; camera->setAllowEventFocus(true); - const SGPropertyNode *clustered = root->getChild("clustered-forward"); - if (clustered) { - int tile_size = clustered->getIntValue("tile-size", 64); - camera->setInitialDrawCallback(new ClusteredForwardDrawCallback(tile_size)); - } + const SGPropertyNode *p_clustered = root->getNode("clustered-shading"); + ClusteredShading *clustered = 0; + if (p_clustered) + clustered = new ClusteredShading(camera, p_clustered); + + camera->setCullCallback(new SceneCullCallback(clustered)); int cubemap_face = root->getIntValue("cubemap-face", -1); float zNear = root->getFloatValue("z-near", 0.0f); From b7f8fbe7a05fb7bd8819d78977a58f463e11b865 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Tue, 11 Jun 2019 12:38:49 +0200 Subject: [PATCH 23/39] Nasal lib: dosprintf rework to measure the required length (vsnsprintf does this with a size of zero and a nullptr as the first parameters) - and then just alloc the required space. Should be more efficient than the looping version. --- simgear/nasal/lib.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/simgear/nasal/lib.c b/simgear/nasal/lib.c index 528803b5..87e6bd6b 100644 --- a/simgear/nasal/lib.c +++ b/simgear/nasal/lib.c @@ -295,28 +295,31 @@ static naRef f_die(naContext c, naRef me, int argc, naRef* args) return naNil(); // never executes } -// Wrapper around vsnprintf, iteratively increasing the buffer size -// until it fits. Returned buffer should be freed by the caller. +// Wrapper around vsnprintf that will allocate the required size +// by calling vsnprintf with NULL and 0 - and vsnsprintf will measure the +// required amount of characters which we then allocate and return +// Returned buffer should be freed by the caller. static char* dosprintf(char* f, ...) { char* buf; va_list va; - int olen, len = 16; + int len = 0; va_start(va, f); - while(1) { - buf = naAlloc(len); - va_list vaCopy; - va_copy(vaCopy, va); - olen = vsnprintf(buf, len, f, vaCopy); - if(olen >= 0 && olen < len) { - va_end(va); - va_end(vaCopy); - return buf; - } - va_end(vaCopy); - naFree(buf); - len *= 2; + va_list vaCopy; + va_copy(vaCopy, va); + len = vsnprintf(0, 0, f, vaCopy); + if (len <= 0) { + buf = naAlloc(2); + *buf = 0; } + else { + len++;// allow for terminating null + buf = naAlloc(len); + len = vsnprintf(buf, len, f, vaCopy); + } + va_end(va); + va_end(vaCopy); + return buf; } // Inspects a printf format string f, and finds the next "%..." format From c71f2874983b04d42136d9a2e779819dc0945d7b Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Tue, 11 Jun 2019 12:42:16 +0200 Subject: [PATCH 24/39] SG Threading added new class for an exclusive thread An exclusive thread is one that is suited to being used where the thread needs to be activated at a certain point, and also that the code activating the thread may also need to wait for thread completion. Example of this is the new background garbage collection for Nasal. The thread will be activated at the end of the frame processing and at the start of the next frame the thread can be awaited - thus allowing the thread to work in parallel with the rendering. --- simgear/threads/SGThread.cxx | 96 ++++++++++++++++++++++++++++++++++++ simgear/threads/SGThread.hxx | 41 +++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/simgear/threads/SGThread.cxx b/simgear/threads/SGThread.cxx index 0ba71f40..c5686e51 100644 --- a/simgear/threads/SGThread.cxx +++ b/simgear/threads/SGThread.cxx @@ -25,6 +25,7 @@ #endif #include +#include #include "SGThread.hxx" @@ -416,3 +417,98 @@ SGWaitCondition::broadcast() { _privateData->broadcast(); } + + +SGExclusiveThread::SGExclusiveThread() : + _started(false), _terminated(false), last_await_time(0), + dataReady(false), complete(true), process_ran(false), process_running(false) + { + } + + SGExclusiveThread::~SGExclusiveThread() + { + + } + + void SGExclusiveThread::release() { + std::unique_lock lck(mutex_); + if (!complete) { + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] not finished - skipping"); + return; + } + if (!complete.exchange(false)) + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] concurrent failure (2)"); + if (dataReady.exchange(true)) + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] concurrent failure (1)"); + condVar.notify_one(); + } + void SGExclusiveThread::wait() { + std::unique_lock lck(mutex_); + if (!dataReady) + { + do + { + condVar.wait(lck); + } while (!dataReady); + } + } + void SGExclusiveThread::clearAwaitCompletionTime() { + last_await_time = 0; + } + void SGExclusiveThread::awaitCompletion() { + timestamp.stamp(); + std::unique_lock lck(Cmutex_); + if (!complete) + { + do { + CcondVar.wait(lck); + } while (!complete.load()); + } + + if (process_ran) { + last_await_time = timestamp.elapsedUSec(); + process_ran = 0; + } + } + + void SGExclusiveThread::setCompletion() { + std::unique_lock lck(Cmutex_); + if (!dataReady.exchange(false)) + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] atomic operation on dataReady failed (5)\n"); + + if (complete.exchange(true)) + SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] atomic operation on complete failed (5)\n"); + CcondVar.notify_one(); + } + void SGExclusiveThread::run() + { + process_running = true; + while (!_terminated) { + wait(); + process_ran = process(); + setCompletion(); + } + process_running = false; + _terminated = false; + _started = false; + } + + void SGExclusiveThread::terminate() { + _terminated = true; + } + bool SGExclusiveThread::stop() + { + return true; + } + void SGExclusiveThread::ensure_running() + { + if (!_started) + { + _started = true; + start(); + } + } + bool SGExclusiveThread::is_running() + { + return process_running; + } diff --git a/simgear/threads/SGThread.hxx b/simgear/threads/SGThread.hxx index d905c624..e5610f4b 100644 --- a/simgear/threads/SGThread.hxx +++ b/simgear/threads/SGThread.hxx @@ -23,7 +23,11 @@ #ifndef SGTHREAD_HXX_INCLUDED #define SGTHREAD_HXX_INCLUDED 1 +#include +#include +#include #include +#include /** * Encapsulate generic threading methods. @@ -184,4 +188,41 @@ private: PrivateData* _privateData; }; +/// +/// an exclusive thread is one that is designed for frame processing; +/// it has the ability to synchronise such that the caller can await +/// the processing to finish. +class SGExclusiveThread : public SGThread{ +private: + std::mutex mutex_; + std::condition_variable condVar; + SGTimeStamp timestamp; + std::mutex Cmutex_; + std::condition_variable CcondVar; + + bool _started; + bool _terminated; + int last_await_time; + + std::atomic dataReady; + std::atomic complete; + std::atomic process_ran; + std::atomic process_running; + +public: + SGExclusiveThread(); + virtual ~SGExclusiveThread(); + void release(); + void wait(); + void clearAwaitCompletionTime(); + virtual void awaitCompletion(); + void setCompletion(); + virtual int process() = 0; + virtual void run(); + void terminate(); + bool stop(); + void ensure_running(); + bool is_running(); +}; + #endif /* SGTHREAD_HXX_INCLUDED */ From 92a3c8bbd823ce2aee5cbe434cc0ea5c03c47f71 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Tue, 11 Jun 2019 13:44:52 +0200 Subject: [PATCH 25/39] Added Nasal garbage collection background thread This uses an SGThreadExclusive controlled by Emesary notifications that are received from the main loop. When active at the end of a frame the garbage collection thread will be released; if it is already running this will do nothing. Optionally at the start of the mainloop we can wait for the previous GC to finish. The actions of the background GC is controlled by notifications - again received from the main loop which in turn uses properties. I initially thought that the wait at the start of the frame would be necessary; however in 100 or so hours of flight without the await for completion at the start of frame no threading problems (or any other problems) were shown; so nasal-gc-threaded-wait is defaulted to false which gives a slight boost in performance. So what this does is to it removes the GC pause of 10-20ms every 4 seconds (test using the F-15). This change doesn't really give much extra performance per frame because normally GC is only performed when needed. --- simgear/nasal/code.c | 16 +- simgear/nasal/cppbind/CMakeLists.txt | 1 + .../nasal/cppbind/NasalEmesaryInterface.hxx | 123 ++++++++++++ .../nasal/cppbind/detail/to_nasal_helper.cxx | 14 ++ simgear/nasal/gc.c | 176 ++++++++++++++++-- 5 files changed, 305 insertions(+), 25 deletions(-) create mode 100644 simgear/nasal/cppbind/NasalEmesaryInterface.hxx diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index faf534ec..0783b38b 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -157,7 +157,7 @@ static void initContext(naContext c) c->error[0] = 0; c->userData = 0; } - +#define BASE_SIZE 256000 static void initGlobals() { int i; @@ -168,10 +168,10 @@ static void initGlobals() globals->sem = naNewSem(); globals->lock = naNewLock(); - globals->allocCount = 256; // reasonable starting value + globals->allocCount = BASE_SIZE; // reasonable starting value for(i=0; ipools[i]), i); - globals->deadsz = 256; + globals->deadsz = BASE_SIZE; globals->ndead = 0; globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz); @@ -833,9 +833,13 @@ naRef naGetSourceFile(naContext ctx, int frame) { naRef f; frame = findFrame(ctx, &ctx, frame); - f = ctx->fStack[frame].func; - f = PTR(f).func->code; - return PTR(f).code->srcFile; + if (frame >= 0) { + f = ctx->fStack[frame].func; + f = PTR(f).func->code; + if (!IS_NIL(f) && PTR(f).code) + return PTR(f).code->srcFile; + } + return naNil(); } char* naGetError(naContext ctx) diff --git a/simgear/nasal/cppbind/CMakeLists.txt b/simgear/nasal/cppbind/CMakeLists.txt index 08f09937..ca2475c7 100644 --- a/simgear/nasal/cppbind/CMakeLists.txt +++ b/simgear/nasal/cppbind/CMakeLists.txt @@ -5,6 +5,7 @@ set(HEADERS Ghost.hxx NasalCallContext.hxx NasalContext.hxx + NasalEmesaryInterface.hxx NasalHash.hxx NasalMe.hxx NasalMethodHolder.hxx diff --git a/simgear/nasal/cppbind/NasalEmesaryInterface.hxx b/simgear/nasal/cppbind/NasalEmesaryInterface.hxx new file mode 100644 index 00000000..3f8d6c1f --- /dev/null +++ b/simgear/nasal/cppbind/NasalEmesaryInterface.hxx @@ -0,0 +1,123 @@ +#ifndef NASALEMESARYINTERFACE_INCLUDED +#define NASALEMESARYINTERFACE_INCLUDED 1 +// Nasal Emesary receipient interface. +// +// Copyright (C) 2019 Richard Harrison rjh@zaretto.com +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include + + +namespace nasal +{ + extern"C" { + extern int GCglobalAlloc(); + extern int naGarbageCollect(); + // these are used by the detailed debug in the Nasal GC. + SGTimeStamp global_timestamp; + void global_stamp() { + global_timestamp.stamp(); + } + extern int global_elapsedUSec() + { + return global_timestamp.elapsedUSec(); + } + } + + class ThreadedGarbageCollector : public SGExclusiveThread { + public: + ThreadedGarbageCollector() : SGExclusiveThread() {} + virtual ~ThreadedGarbageCollector() {} + + virtual int process(){ + return naGarbageCollect(); + } + }; + + class NasalMainLoopRecipient : public simgear::Emesary::IReceiver { + public: + NasalMainLoopRecipient() : receiveCount(0) { + simgear::Emesary::GlobalTransmitter::instance()->Register(*this); + SG_LOG(SG_NASAL, SG_INFO, "NasalMainLoopRecipient created"); + } + virtual ~NasalMainLoopRecipient() { + simgear::Emesary::GlobalTransmitter::instance()->DeRegister(*this); + } + + std::atomic receiveCount; + virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n) + { + + simgear::Notifications::MainLoopNotification *mln = dynamic_cast(&n); + + if (mln) { + switch (mln->GetValue()) { + case simgear::Notifications::MainLoopNotification::Type::Begin: + if (gct.is_running()) { + if (Active && CanWait) + gct.awaitCompletion(); + else + gct.clearAwaitCompletionTime(); + } + break; + case simgear::Notifications::MainLoopNotification::Type::End: + if (Active) { + if (gct.is_running()) + gct.release(); + } + break; + case simgear::Notifications::MainLoopNotification::Type::Started: + gct.ensure_running(); + break; + case simgear::Notifications::MainLoopNotification::Type::Stopped: + gct.terminate(); + break; + } + return simgear::Emesary::ReceiptStatusOK; + } + + auto *gccn = dynamic_cast(&n); + if (gccn) { + CanWait = gccn->GetCanWait(); + Active = gccn->GetActive(); + return simgear::Emesary::ReceiptStatusOK; + } + return simgear::Emesary::ReceiptStatusNotProcessed; + } + protected: + bool CanWait; + bool Active; + ThreadedGarbageCollector gct; + }; + +} // namespace nasal +#endif diff --git a/simgear/nasal/cppbind/detail/to_nasal_helper.cxx b/simgear/nasal/cppbind/detail/to_nasal_helper.cxx index 752a7d5a..26d3363f 100644 --- a/simgear/nasal/cppbind/detail/to_nasal_helper.cxx +++ b/simgear/nasal/cppbind/detail/to_nasal_helper.cxx @@ -19,6 +19,7 @@ #include "to_nasal_helper.hxx" #include #include +#include #include #include @@ -27,6 +28,19 @@ namespace nasal { + // create single instance of the main loop recipient for Nasal - this will self register at the + // global transmitter - and that's all that is needed to link up the background GC to the main + // loop in FG that will send out the MainLoop notifications. + //class NasalMainLoopRecipientSingleton : public simgear::Singleton + //{ + //public: + // NasalMainLoopRecipientSingleton() + // { + // } + // virtual ~NasalMainLoopRecipientSingleton() {} + //}; + NasalMainLoopRecipient mrl; + //---------------------------------------------------------------------------- naRef to_nasal_helper(naContext c, const std::string& str) { diff --git a/simgear/nasal/gc.c b/simgear/nasal/gc.c index 5ac9c43c..67a0100b 100644 --- a/simgear/nasal/gc.c +++ b/simgear/nasal/gc.c @@ -1,7 +1,6 @@ #include "nasal.h" #include "data.h" #include "code.h" - #define MIN_BLOCK_SIZE 32 static void reap(struct naPool* p); @@ -12,14 +11,17 @@ struct Block { char* block; struct Block* next; }; - // Must be called with the giant exclusive lock! -static void freeDead() +extern void global_stamp(); +extern int global_elapsedUSec(); + +static int freeDead() { int i; for(i=0; indead; i++) naFree(globals->deadBlocks[i]); globals->ndead = 0; + return i; } static void marktemps(struct Context* c) @@ -31,50 +33,127 @@ static void marktemps(struct Context* c) mark(r); } } - +//#define GC_DETAIL_DEBUG +static int __elements_visited = 0; +static int gc_busy=0; // Must be called with the big lock! static void garbageCollect() { + if (gc_busy) + return; + gc_busy = 1; int i; struct Context* c; globals->allocCount = 0; c = globals->allContexts; - while(c) { - for(i=0; iallContexts; + while (c) { +#if GC_DETAIL_DEBUG + ctxc++; +#endif + for (i = 0; i < NUM_NASAL_TYPES; i++) c->nfree[i] = 0; - for(i=0; i < c->fTop; i++) { + for (i = 0; i < c->fTop; i++) { mark(c->fStack[i].func); mark(c->fStack[i].locals); } - for(i=0; i < c->opTop; i++) + for (i = 0; i < c->opTop; i++) mark(c->opStack[i]); mark(c->dieArg); marktemps(c); c = c->nextAll; } +#if GC_DETAIL_DEBUG + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + printf("--> garbageCollect(#e%-5d): %-4d ", eel, et); +#endif mark(globals->save); +#if GC_DETAIL_DEBUG + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + printf("s(%5d) %-5d ", eel, et); +#endif + mark(globals->save_hash); +#if GC_DETAIL_DEBUG + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + printf("h(%5d) %-5d ", eel, et); +#endif + + mark(globals->symbols); +#if GC_DETAIL_DEBUG + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + //printf("sy(%5d) %-4d ", eel, et); +#endif + mark(globals->meRef); +#if GC_DETAIL_DEBUG + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + //printf("me(%5d) %-5d ", eel, et); +#endif + mark(globals->argRef); +#if GC_DETAIL_DEBUG + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; + //printf("ar(%5d) %-5d ", eel, et); +#endif + mark(globals->parentsRef); - +#if GC_DETAIL_DEBUG + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + eel = __elements_visited - stel; stel = __elements_visited; +#endif + //printf(" ev[%3d] %-5d", eel, et); // Finally collect all the freed objects - for(i=0; ipools[i])); - + } +#if GC_DETAIL_DEBUG + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + printf(" >> reap %-5d", et); +#endif // Make enough space for the dead blocks we need to free during // execution. This works out to 1 spot for every 2 live objects, // which should be limit the number of bottleneck operations // without imposing an undue burden of extra "freeable" memory. if(globals->deadsz < globals->allocCount) { globals->deadsz = globals->allocCount; - if(globals->deadsz < 256) globals->deadsz = 256; + if(globals->deadsz < 256000) globals->deadsz = 256000; naFree(globals->deadBlocks); globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz); } globals->needGC = 0; +#if GC_DETAIL_DEBUG + et = global_elapsedUSec() - st; + st = global_elapsedUSec(); + printf(">> %-5d ", et); +#endif + gc_busy = 0; } void naModLock() @@ -104,6 +183,7 @@ void naModUnlock() // you think about it). static void bottleneck() { + global_stamp(); struct Globals* g = globals; g->bottleneck = 1; while(g->bottleneck && g->waitCount < g->nThreads - 1) { @@ -111,12 +191,39 @@ static void bottleneck() UNLOCK(); naSemDown(g->sem); LOCK(); g->waitCount--; } +#if GC_DETAIL_DEBUG + printf("GC: wait %2d ", global_elapsedUSec()); +#endif if(g->waitCount >= g->nThreads - 1) { - freeDead(); - if(g->needGC) garbageCollect(); + int fd = freeDead(); +#if GC_DETAIL_DEBUG + printf("--> freedead (%5d) : %5d", fd, global_elapsedUSec()); +#endif + if(g->needGC) + garbageCollect(); if(g->waitCount) naSemUp(g->sem, g->waitCount); g->bottleneck = 0; } +#if GC_DETAIL_DEBUG + printf(" :: finished: %5d\n", global_elapsedUSec()); +#endif +} + +static void bottleneckFreeDead() +{ + global_stamp(); + struct Globals* g = globals; + g->bottleneck = 1; + while (g->bottleneck && g->waitCount < g->nThreads - 1) { + g->waitCount++; + UNLOCK(); naSemDown(g->sem); LOCK(); + g->waitCount--; + } + if (g->waitCount >= g->nThreads - 1) { + freeDead(); + if (g->waitCount) naSemUp(g->sem, g->waitCount); + g->bottleneck = 0; + } } void naGC() @@ -127,6 +234,29 @@ void naGC() UNLOCK(); naCheckBottleneck(); } +int naGarbageCollect() +{ + int rv = 1; + LOCK(); + // + // The number here is again based on observation - if this is too low then the inline GC will be used + // which is fine occasionally. + // So what we're doing by checking the global alloc is to see if GC is likely required during the next frame and if + // so we pre-empt this by doing it now. + // GC can typically take between 5ms and 50ms (F-15, FG1000 PFD & MFD, Advanced weather) - but usually it is completed + // prior to the start of the next frame. + + globals->needGC = nasal_globals->allocCount < 23000; + if (globals->needGC) + bottleneck(); + else { + bottleneckFreeDead(); + rv = 0; + } + UNLOCK(); + naCheckBottleneck(); + return rv; +} void naCheckBottleneck() { @@ -207,7 +337,9 @@ static int poolsize(struct naPool* p) while(b) { total += b->size; b = b->next; } return total; } - +int GCglobalAlloc() { + return globals->allocCount; +} struct naObj** naGC_get(struct naPool* p, int n, int* nout) { struct naObj** result; @@ -215,6 +347,9 @@ struct naObj** naGC_get(struct naPool* p, int n, int* nout) LOCK(); while(globals->allocCount < 0 || (p->nfree == 0 && p->freetop >= p->freesz)) { globals->needGC = 1; +#if GC_DETAIL_DEBUG + printf("++"); +#endif bottleneck(); } if(p->nfree == 0) @@ -248,7 +383,7 @@ static void mark(naRef r) if(PTR(r).obj->mark == 1) return; - + __elements_visited++; PTR(r).obj->mark = 1; switch(PTR(r).obj->type) { case T_VEC: markvec(r); break; @@ -306,11 +441,14 @@ static void reap(struct naPool* p) // Allocate more if necessary (try to keep 25-50% of the objects // available) - if(p->nfree < total/4) { + // This was changed (2019.2) to allocate in larger blocks + // previously it used total/4 and used/2 now we + // use total/2 and used / 1 + if (p->nfree < total / 2) { int used = total - p->nfree; int avail = total - used; - int need = used/2 - avail; - if(need > 0) + int need = used / 1 - avail; + if (need > 0) newBlock(p, need); } } From ab8b1d39bdc4b47eb3e5a00a186a2c4b7f27f29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Tue, 11 Jun 2019 23:05:09 +0200 Subject: [PATCH 26/39] Fixed undetected compiler error due to gcc accepting variable-length arrays --- simgear/scene/viewer/ClusteredShading.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/simgear/scene/viewer/ClusteredShading.cxx b/simgear/scene/viewer/ClusteredShading.cxx index dcd13720..e42883a2 100644 --- a/simgear/scene/viewer/ClusteredShading.cxx +++ b/simgear/scene/viewer/ClusteredShading.cxx @@ -253,12 +253,12 @@ ClusteredShading::update(const SGLightList &light_list) // Again, avoid the unnecessary threading overhead threadFunc(0); } else { - std::thread light_threads[_num_threads]; + std::vector threads; + threads.reserve(_num_threads); for (int i = 0; i < _num_threads; ++i) - light_threads[i] = std::thread(&ClusteredShading::threadFunc, this, i); + threads.emplace_back(&ClusteredShading::threadFunc, this, i); - for (int i = 0; i < _num_threads; ++i) - light_threads[i].join(); + for (auto &t : threads) t.join(); } // Force upload of the image data From 01ac9a71b77e4405d0dbdbd3aa822269870c9d2a Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Tue, 11 Jun 2019 23:39:14 +0200 Subject: [PATCH 27/39] Fix case sensitive filename for emesary.cxx --- simgear/emesary/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simgear/emesary/CMakeLists.txt b/simgear/emesary/CMakeLists.txt index 13ad790d..30ca3318 100644 --- a/simgear/emesary/CMakeLists.txt +++ b/simgear/emesary/CMakeLists.txt @@ -13,7 +13,7 @@ set(HEADERS ) set(SOURCES - emesary.cxx + Emesary.cxx ) simgear_component(emesary emesary "${SOURCES}" "${HEADERS}") From c0677ad8c5b90bbd0c349edb6b01e26833f251be Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Fri, 14 Jun 2019 21:43:47 -0500 Subject: [PATCH 28/39] [Emesary] Fix compile on Linux. --- simgear/emesary/Transmitter.hxx | 1 + simgear/emesary/test_emesary.cxx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/simgear/emesary/Transmitter.hxx b/simgear/emesary/Transmitter.hxx index cb67bc64..339a5488 100644 --- a/simgear/emesary/Transmitter.hxx +++ b/simgear/emesary/Transmitter.hxx @@ -21,6 +21,7 @@ * *---------------------------------------------------------------------------*/ +#include #include #include #include diff --git a/simgear/emesary/test_emesary.cxx b/simgear/emesary/test_emesary.cxx index cca55288..852fa55b 100644 --- a/simgear/emesary/test_emesary.cxx +++ b/simgear/emesary/test_emesary.cxx @@ -7,14 +7,14 @@ #include -#include +#include using std::cout; using std::cerr; using std::endl; -std::atomic nthread = 0; -std::atomic noperations = 0; +std::atomic nthread {0}; +std::atomic noperations {0}; const int MaxIterations = 9999; class TestThreadNotification : public simgear::Emesary::INotification From 2cbbb29eb534e634dd42049399e504b1e7aa8389 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Fri, 14 Jun 2019 22:11:31 -0500 Subject: [PATCH 29/39] [Emesary] Fix compile on Linux. --- simgear/emesary/Transmitter.hxx | 1 + simgear/emesary/test_emesary.cxx | 6 +++--- simgear/nasal/cppbind/NasalEmesaryInterface.hxx | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/simgear/emesary/Transmitter.hxx b/simgear/emesary/Transmitter.hxx index cb67bc64..339a5488 100644 --- a/simgear/emesary/Transmitter.hxx +++ b/simgear/emesary/Transmitter.hxx @@ -21,6 +21,7 @@ * *---------------------------------------------------------------------------*/ +#include #include #include #include diff --git a/simgear/emesary/test_emesary.cxx b/simgear/emesary/test_emesary.cxx index cca55288..852fa55b 100644 --- a/simgear/emesary/test_emesary.cxx +++ b/simgear/emesary/test_emesary.cxx @@ -7,14 +7,14 @@ #include -#include +#include using std::cout; using std::cerr; using std::endl; -std::atomic nthread = 0; -std::atomic noperations = 0; +std::atomic nthread {0}; +std::atomic noperations {0}; const int MaxIterations = 9999; class TestThreadNotification : public simgear::Emesary::INotification diff --git a/simgear/nasal/cppbind/NasalEmesaryInterface.hxx b/simgear/nasal/cppbind/NasalEmesaryInterface.hxx index 3f8d6c1f..b6d01ca1 100644 --- a/simgear/nasal/cppbind/NasalEmesaryInterface.hxx +++ b/simgear/nasal/cppbind/NasalEmesaryInterface.hxx @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include From 3b3093c72eaf3cacbb0a1411b9551706e52009bf Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sat, 15 Jun 2019 11:54:35 -0500 Subject: [PATCH 30/39] [nasal] remediate segfault in lib.c state of vaCopy is altered during each usage, so it needs to be discarded and not reused by multiple calls. --- simgear/nasal/lib.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/simgear/nasal/lib.c b/simgear/nasal/lib.c index 87e6bd6b..0322492f 100644 --- a/simgear/nasal/lib.c +++ b/simgear/nasal/lib.c @@ -302,23 +302,28 @@ static naRef f_die(naContext c, naRef me, int argc, naRef* args) static char* dosprintf(char* f, ...) { char* buf; + va_list va; - int len = 0; va_start(va, f); - va_list vaCopy; - va_copy(vaCopy, va); - len = vsnprintf(0, 0, f, vaCopy); + + int len = vsnprintf(0, 0, f, va); + va_end(va); + if (len <= 0) { - buf = naAlloc(2); + buf = (char *) naAlloc(2); *buf = 0; } else { len++;// allow for terminating null - buf = naAlloc(len); - len = vsnprintf(buf, len, f, vaCopy); + buf = (char *) naAlloc(len); + + va_list va; + va_start(va, f); + + len = vsnprintf(buf, len, f, va); + va_end(va); } - va_end(va); - va_end(vaCopy); + return buf; } From d8a46cffa5198260e980b93ffb4899665b65553d Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sat, 15 Jun 2019 17:22:53 -0500 Subject: [PATCH 31/39] [emesary] Fix naming conflict with some compilers --- simgear/emesary/notifications.hxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/simgear/emesary/notifications.hxx b/simgear/emesary/notifications.hxx index bc8fd12e..0c98e3c6 100644 --- a/simgear/emesary/notifications.hxx +++ b/simgear/emesary/notifications.hxx @@ -16,7 +16,7 @@ * * Version : $Header: $ * -* Copyright © 2002 - 2017 Richard Harrison All Rights Reserved. +* Copyright � 2002 - 2017 Richard Harrison All Rights Reserved. * *---------------------------------------------------------------------------*/ @@ -30,13 +30,13 @@ namespace simgear { public: enum Type { Started, Stopped, Begin, End }; - MainLoopNotification(Type v) : Type(v) {} + MainLoopNotification(Type v) : _type(v) {} - virtual Type GetValue() { return Type; } + virtual Type GetValue() { return _type; } virtual const char *GetType() { return "MainLoop"; } protected: - Type Type; + Type _type; }; class NasalGarbageCollectionConfigurationNotification : public simgear::Emesary::INotification From b73dfb08aefa745e72885af27c66b93eafb9f8e5 Mon Sep 17 00:00:00 2001 From: Edward d'Auvergne Date: Tue, 3 Apr 2018 11:59:46 +0200 Subject: [PATCH 32/39] SGSubsystem classes: Whitespace standardisation of the declarations. This is a clean up commit prior to the subsystem API standardisation to simplify the diffs. It includes all SGSubsystem and SGSubsystemGroup derived classes. --- simgear/canvas/CanvasMgr.hxx | 61 ++++--- simgear/misc/interpolator.hxx | 3 +- simgear/props/PropertyBasedMgr.hxx | 105 ++++++----- simgear/props/PropertyInterpolationMgr.hxx | 192 ++++++++++----------- simgear/scene/tsync/terrasync.hxx | 2 +- simgear/sound/soundmgr.hxx | 4 +- simgear/structure/SGPerfMon.hxx | 1 - simgear/structure/event_mgr.hxx | 23 ++- simgear/structure/subsystem_mgr.hxx | 30 ++-- 9 files changed, 209 insertions(+), 212 deletions(-) diff --git a/simgear/canvas/CanvasMgr.hxx b/simgear/canvas/CanvasMgr.hxx index 3255d4bc..e689a7f4 100644 --- a/simgear/canvas/CanvasMgr.hxx +++ b/simgear/canvas/CanvasMgr.hxx @@ -28,42 +28,39 @@ namespace simgear namespace canvas { - class CanvasMgr: - public PropertyBasedMgr - { - public: +class CanvasMgr : public PropertyBasedMgr +{ +public: + /** + * @param node Root node of branch used to control canvasses + */ + CanvasMgr(SGPropertyNode_ptr node); - /** - * @param node Root node of branch used to control canvasses - */ - CanvasMgr(SGPropertyNode_ptr node); + /** + * Create a new canvas + * + * @param name Name of the new canvas + */ + CanvasPtr createCanvas(const std::string& name = ""); - /** - * Create a new canvas - * - * @param name Name of the new canvas - */ - CanvasPtr createCanvas(const std::string& name = ""); + /** + * Get ::Canvas by index + * + * @param index Index of texture node in /canvas/by-index/ + */ + CanvasPtr getCanvas(size_t index) const; - /** - * Get ::Canvas by index - * - * @param index Index of texture node in /canvas/by-index/ - */ - CanvasPtr getCanvas(size_t index) const; + /** + * Get ::Canvas by name + * + * @param name Value of child node "name" in + * /canvas/by-index/texture[i]/name + */ + CanvasPtr getCanvas(const std::string& name) const; - /** - * Get ::Canvas by name - * - * @param name Value of child node "name" in - * /canvas/by-index/texture[i]/name - */ - CanvasPtr getCanvas(const std::string& name) const; - - protected: - - void elementCreated(PropertyBasedElementPtr element) override; - }; +protected: + void elementCreated(PropertyBasedElementPtr element) override; +}; } // namespace canvas } // namespace simgear diff --git a/simgear/misc/interpolator.hxx b/simgear/misc/interpolator.hxx index e0721c6c..9a5bdcfa 100644 --- a/simgear/misc/interpolator.hxx +++ b/simgear/misc/interpolator.hxx @@ -30,7 +30,8 @@ // code can register another one immediately without worrying about // timer aliasing. -class SGInterpolator : public SGSubsystem { +class SGInterpolator : public SGSubsystem +{ public: SGInterpolator() { _list = 0; } virtual void init() {} diff --git a/simgear/props/PropertyBasedMgr.hxx b/simgear/props/PropertyBasedMgr.hxx index 9a14dab5..e058206b 100644 --- a/simgear/props/PropertyBasedMgr.hxx +++ b/simgear/props/PropertyBasedMgr.hxx @@ -29,74 +29,71 @@ namespace simgear { - class PropertyBasedMgr: - public SGSubsystem, - public SGPropertyChangeListener - { - public: - void init() override; - void shutdown() override; +class PropertyBasedMgr : public SGSubsystem, + public SGPropertyChangeListener +{ +public: + void init() override; + void shutdown() override; - void update (double delta_time_sec) override; + void update(double delta_time_sec) override; - /** - * Create a new PropertyBasedElement - * - * @param name Name of the new element - */ - PropertyBasedElementPtr createElement(const std::string& name = ""); + /** + * Create a new PropertyBasedElement + * + * @param name Name of the new element + */ + PropertyBasedElementPtr createElement(const std::string& name = ""); - /** - * Get an existing PropertyBasedElement by its index - * - * @param index Index of element node in property tree - */ - PropertyBasedElementPtr getElement(size_t index) const; + /** + * Get an existing PropertyBasedElement by its index + * + * @param index Index of element node in property tree + */ + PropertyBasedElementPtr getElement(size_t index) const; - /** - * Get an existing PropertyBasedElement by its name - * - * @param name Name (value of child node "name" will be matched) - */ - PropertyBasedElementPtr getElement(const std::string& name) const; + /** + * Get an existing PropertyBasedElement by its name + * + * @param name Name (value of child node "name" will be matched) + */ + PropertyBasedElementPtr getElement(const std::string& name) const; - virtual const SGPropertyNode* getPropertyRoot() const; + virtual const SGPropertyNode* getPropertyRoot() const; - protected: +protected: + typedef boost::function + ElementFactory; - typedef boost::function - ElementFactory; + /** Branch in the property tree for this property managed subsystem */ + SGPropertyNode_ptr _props; - /** Branch in the property tree for this property managed subsystem */ - SGPropertyNode_ptr _props; + /** Property name of managed elements */ + const std::string _name_elements; - /** Property name of managed elements */ - const std::string _name_elements; + /** The actually managed elements */ + std::vector _elements; - /** The actually managed elements */ - std::vector _elements; + /** Function object which creates a new element */ + ElementFactory _element_factory; - /** Function object which creates a new element */ - ElementFactory _element_factory; + /** + * @param props Root node of property branch used for controlling + * this subsystem + * @param name_elements The name of the nodes for the managed elements + */ + PropertyBasedMgr( SGPropertyNode_ptr props, + const std::string& name_elements, + ElementFactory element_factory ); + virtual ~PropertyBasedMgr() = 0; - /** - * @param props Root node of property branch used for controlling - * this subsystem - * @param name_elements The name of the nodes for the managed elements - */ - PropertyBasedMgr( SGPropertyNode_ptr props, - const std::string& name_elements, - ElementFactory element_factory ); - virtual ~PropertyBasedMgr() = 0; - - void childAdded( SGPropertyNode * parent, + void childAdded( SGPropertyNode * parent, + SGPropertyNode * child ) override; + void childRemoved( SGPropertyNode * parent, SGPropertyNode * child ) override; - void childRemoved( SGPropertyNode * parent, - SGPropertyNode * child ) override; - virtual void elementCreated(PropertyBasedElementPtr element) {} - - }; + virtual void elementCreated(PropertyBasedElementPtr element) {} +}; } // namespace simgear diff --git a/simgear/props/PropertyInterpolationMgr.hxx b/simgear/props/PropertyInterpolationMgr.hxx index efd907d9..51ec15d7 100644 --- a/simgear/props/PropertyInterpolationMgr.hxx +++ b/simgear/props/PropertyInterpolationMgr.hxx @@ -28,119 +28,115 @@ #include -namespace simgear +namespace simgear { + +/** + * Subsystem that manages interpolation of properties. + * + * By default the numeric values of the properties are interpolated. For + * example, for strings this is probably not the wanted behavior. For this + * adapter classes can be registered to allow providing specific + * interpolations for certain types of properties. Using the type "color", + * provided by ColorInterpolator, strings containing %CSS colors can also be + * interpolated. + * + * Additionally different functions can be used for easing of the animation. + * By default "linear" (constant animation speed) and "swing" (smooth + * acceleration and deceleration) are available. + */ +class PropertyInterpolationMgr : public SGSubsystem { +public: + typedef PropertyInterpolator* (*InterpolatorFactory)(); - /** - * Subsystem that manages interpolation of properties. - * - * By default the numeric values of the properties are interpolated. For - * example, for strings this is probably not the wanted behavior. For this - * adapter classes can be registered to allow providing specific - * interpolations for certain types of properties. Using the type "color", - * provided by ColorInterpolator, strings containing %CSS colors can also be - * interpolated. - * - * Additionally different functions can be used for easing of the animation. - * By default "linear" (constant animation speed) and "swing" (smooth - * acceleration and deceleration) are available. - */ - class PropertyInterpolationMgr: - public SGSubsystem - { - public: - typedef PropertyInterpolator* (*InterpolatorFactory)(); + PropertyInterpolationMgr(); - PropertyInterpolationMgr(); + /** + * Update all active interpolators. + */ + void update(double dt); - /** - * Update all active interpolators. - */ - void update(double dt); + /** + * Create a new property interpolator. + * + * @note To actually use it the interpolator needs to be attached to a + * property using PropertyInterpolationMgr::interpolate. + * + * @param type Type of animation ("numeric", "color", etc.) + * @param target Property containing target value + * @param duration Duration if the animation (in seconds) + * @param easing Type of easing ("linear", "swing", etc.) + */ + PropertyInterpolator* + createInterpolator(const std::string& type, + const SGPropertyNode& target, + double duration, + const std::string& easing); - /** - * Create a new property interpolator. - * - * @note To actually use it the interpolator needs to be attached to a - * property using PropertyInterpolationMgr::interpolate. - * - * @param type Type of animation ("numeric", "color", etc.) - * @param target Property containing target value - * @param duration Duration if the animation (in seconds) - * @param easing Type of easing ("linear", "swing", etc.) - */ - PropertyInterpolator* - createInterpolator( const std::string& type, - const SGPropertyNode& target, - double duration, - const std::string& easing ); + /** + * Add animation of the given property from its current value to the + * target value of the interpolator. If no interpolator is given any + * existing animation of the given property is aborted. + * + * @param prop Property to be interpolated + * @param interp Interpolator used for interpolation + */ + bool interpolate(SGPropertyNode* prop, + PropertyInterpolatorRef interp = 0); - /** - * Add animation of the given property from its current value to the - * target value of the interpolator. If no interpolator is given any - * existing animation of the given property is aborted. - * - * @param prop Property to be interpolated - * @param interp Interpolator used for interpolation - */ - bool interpolate( SGPropertyNode* prop, - PropertyInterpolatorRef interp = 0 ); + bool interpolate(SGPropertyNode* prop, + const std::string& type, + const SGPropertyNode& target, + double duration, + const std::string& easing); - bool interpolate( SGPropertyNode* prop, - const std::string& type, - const SGPropertyNode& target, - double duration, - const std::string& easing ); + bool interpolate(SGPropertyNode* prop, + const std::string& type, + const PropertyList& values, + const double_list& deltas, + const std::string& easing); - bool interpolate( SGPropertyNode* prop, - const std::string& type, - const PropertyList& values, - const double_list& deltas, - const std::string& easing ); - - /** - * Register factory for interpolation type. - */ - void addInterpolatorFactory( const std::string& type, - InterpolatorFactory factory ); - template - void addInterpolatorFactory(const std::string& type) - { - addInterpolatorFactory - ( - type, - &simgear::make_new_derived + /** + * Register factory for interpolation type. + */ + void addInterpolatorFactory(const std::string& type, + InterpolatorFactory factory); + template + void addInterpolatorFactory(const std::string& type) + { + addInterpolatorFactory( + type, + &simgear::make_new_derived ); - } + } - /** - * Register easing function. - */ - void addEasingFunction(const std::string& type, easing_func_t func); + /** + * Register easing function. + */ + void addEasingFunction(const std::string& type, easing_func_t func); - /** - * Set property containing real time delta (not sim time) - * - * TODO better pass both deltas to all update methods... - */ - void setRealtimeProperty(SGPropertyNode* node); + /** + * Set property containing real time delta (not sim time) + * + * TODO better pass both deltas to all update methods... + */ + void setRealtimeProperty(SGPropertyNode* node); - protected: +protected: + typedef std::map InterpolatorFactoryMap; + typedef std::map EasingFunctionMap; + typedef std::pair PropertyInterpolatorPair; + typedef std::list InterpolatorList; - typedef std::map InterpolatorFactoryMap; - typedef std::map EasingFunctionMap; - typedef std::pair< SGPropertyNode*, - PropertyInterpolatorRef > PropertyInterpolatorPair; - typedef std::list InterpolatorList; + struct PredicateIsSameProp; - struct PredicateIsSameProp; + InterpolatorFactoryMap _interpolator_factories; + EasingFunctionMap _easing_functions; + InterpolatorList _interpolators; - InterpolatorFactoryMap _interpolator_factories; - EasingFunctionMap _easing_functions; - InterpolatorList _interpolators; - - SGPropertyNode_ptr _rt_prop; - }; + SGPropertyNode_ptr _rt_prop; +}; } // namespace simgear diff --git a/simgear/scene/tsync/terrasync.hxx b/simgear/scene/tsync/terrasync.hxx index 4e806568..214fdf5a 100644 --- a/simgear/scene/tsync/terrasync.hxx +++ b/simgear/scene/tsync/terrasync.hxx @@ -39,7 +39,6 @@ class BufferedLogCallback; class SGTerraSync : public SGSubsystem { public: - SGTerraSync(); virtual ~SGTerraSync(); @@ -80,6 +79,7 @@ public: void scheduleDataDir(const std::string& dataDir); bool isDataDirPending(const std::string& dataDir) const; + protected: void syncAirportsModels(); string_list getSceneryPathSuffixes() const; diff --git a/simgear/sound/soundmgr.hxx b/simgear/sound/soundmgr.hxx index 89b8cda4..55e683de 100644 --- a/simgear/sound/soundmgr.hxx +++ b/simgear/sound/soundmgr.hxx @@ -49,7 +49,6 @@ class SGSoundSample; class SGSoundMgr : public SGSubsystem { public: - SGSoundMgr(); ~SGSoundMgr(); @@ -310,7 +309,7 @@ public: size_t *size, int *freq, int *block ); - + /** * Get a list of available playback devices. */ @@ -325,6 +324,7 @@ public: bool testForError(std::string s, std::string name = "sound manager"); static const char* subsystemName() { return "sound"; }; + private: class SoundManagerPrivate; /// private implementation object diff --git a/simgear/structure/SGPerfMon.hxx b/simgear/structure/SGPerfMon.hxx index 32d642cc..683beb46 100644 --- a/simgear/structure/SGPerfMon.hxx +++ b/simgear/structure/SGPerfMon.hxx @@ -34,7 +34,6 @@ class SampleStatistic; class SGPerformanceMonitor : public SGSubsystem { - public: SGPerformanceMonitor(SGSubsystemMgr* subSysMgr, SGPropertyNode_ptr root); diff --git a/simgear/structure/event_mgr.hxx b/simgear/structure/event_mgr.hxx index 70d516fb..ea79abcb 100644 --- a/simgear/structure/event_mgr.hxx +++ b/simgear/structure/event_mgr.hxx @@ -8,11 +8,12 @@ class SGEventMgr; -class SGTimer { +class SGTimer +{ public: ~SGTimer(); void run(); - + std::string name; double interval; SGCallback* callback; @@ -20,7 +21,8 @@ public: bool running; }; -class SGTimerQueue { +class SGTimerQueue +{ public: SGTimerQueue(int preSize=1); ~SGTimerQueue(); @@ -37,8 +39,9 @@ public: double nextTime() { return -_table[0].pri; } SGTimer* findByName(const std::string& name) const; - + void dump(); + private: // The "priority" is stored as a negative time. This allows the // implementation to treat the "top" of the heap as the largest @@ -50,9 +53,9 @@ private: int rchild(int n) { return ((n+1)*2 + 1) - 1; } double pri(int n) { return _table[n].pri; } void swap(int a, int b) { - HeapEntry tmp = _table[a]; - _table[a] = _table[b]; - _table[b] = tmp; + HeapEntry tmp = _table[a]; + _table[a] = _table[b]; + _table[b] = tmp; } void siftDown(int n); void siftUp(int n); @@ -77,6 +80,7 @@ public: virtual void update(double delta_time_sec); virtual void unbind(); virtual void shutdown(); + void setRealtimeProperty(SGPropertyNode* node) { _rtProp = node; } /** @@ -119,8 +123,9 @@ public: void removeTask(const std::string& name); - + void dump(); + private: friend class SGTimer; @@ -130,7 +135,7 @@ private: SGPropertyNode_ptr _freezeProp; SGPropertyNode_ptr _rtProp; - SGTimerQueue _rtQueue; + SGTimerQueue _rtQueue; SGTimerQueue _simQueue; bool _inited, _shutdown; }; diff --git a/simgear/structure/subsystem_mgr.hxx b/simgear/structure/subsystem_mgr.hxx index b991a9ef..76b88327 100644 --- a/simgear/structure/subsystem_mgr.hxx +++ b/simgear/structure/subsystem_mgr.hxx @@ -127,6 +127,7 @@ typedef void (*SGSubsystemTimingCb)(void* userData, const std::string& name, Sam * subsystems may also override the suspend() and resume() methods to * take different actions.

*/ + class SGSubsystem : public SGReferenced { public: @@ -342,24 +343,24 @@ public: protected: friend class SGSubsystemMgr; friend class SGSubsystemGroup; - + void set_name(const std::string& n); void set_group(SGSubsystemGroup* group); - + /// composite name for the subsystem (type name and instance name if this /// is an instanced subsystem. (Since this member was originally defined as /// protected, not private, we can't rename it easily) std::string _name; - - bool _suspended = false; - eventTimeVec timingInfo; + bool _suspended = false; - static SGSubsystemTimingCb reportTimingCb; - static void* reportTimingUserData; - static bool reportTimingStatsRequest; - static int maxTimePerFrame_ms; + eventTimeVec timingInfo; + + static SGSubsystemTimingCb reportTimingCb; + static void* reportTimingUserData; + static bool reportTimingStatsRequest; + static int maxTimePerFrame_ms; private: SGSubsystemGroup* _group = nullptr; @@ -416,9 +417,9 @@ public: */ void set_fixed_update_time(double fixed_dt); - /** - * retrive list of member subsystem names - */ + /** + * retrive list of member subsystem names + */ string_list member_names() const; template @@ -431,6 +432,7 @@ public: { return true; } SGSubsystemMgr* get_manager() const override; + private: void forEach(std::function f); void reverseForEach(std::function f); @@ -575,11 +577,11 @@ public: SOFT, PROPERTY }; - + std::string name; Type type; }; - + using DependencyVec = std::vector; using SubsystemFactoryFunctor = std::function; From 10662e4ca5ee924932c355aca89bbc01f0b10817 Mon Sep 17 00:00:00 2001 From: Edward d'Auvergne Date: Tue, 3 Apr 2018 11:45:18 +0200 Subject: [PATCH 33/39] SGSubsystem classes: Subsystem and subsystem group API declaration standardisation. This is a cleanup commit. --- simgear/misc/interpolator.hxx | 5 ++- simgear/props/PropertyBasedMgr.hxx | 2 +- simgear/props/PropertyInterpolationMgr.hxx | 6 +-- simgear/scene/tsync/terrasync.hxx | 13 ++++--- simgear/sound/soundmgr.hxx | 17 +++++---- simgear/structure/SGPerfMon.hxx | 9 +++-- simgear/structure/event_mgr.hxx | 9 +++-- simgear/structure/subsystem_mgr.hxx | 44 +++++++++++----------- 8 files changed, 55 insertions(+), 50 deletions(-) diff --git a/simgear/misc/interpolator.hxx b/simgear/misc/interpolator.hxx index 9a5bdcfa..17f72474 100644 --- a/simgear/misc/interpolator.hxx +++ b/simgear/misc/interpolator.hxx @@ -34,8 +34,9 @@ class SGInterpolator : public SGSubsystem { public: SGInterpolator() { _list = 0; } - virtual void init() {} - virtual void update(double delta_time_sec); + + // Subsystem API. + void update(double delta_time_sec) override; // Simple method that interpolates a double property value from // its current value (default of zero) to the specified target diff --git a/simgear/props/PropertyBasedMgr.hxx b/simgear/props/PropertyBasedMgr.hxx index e058206b..1d40326a 100644 --- a/simgear/props/PropertyBasedMgr.hxx +++ b/simgear/props/PropertyBasedMgr.hxx @@ -33,9 +33,9 @@ class PropertyBasedMgr : public SGSubsystem, public SGPropertyChangeListener { public: + // Subsystem API. void init() override; void shutdown() override; - void update(double delta_time_sec) override; /** diff --git a/simgear/props/PropertyInterpolationMgr.hxx b/simgear/props/PropertyInterpolationMgr.hxx index 51ec15d7..429d2933 100644 --- a/simgear/props/PropertyInterpolationMgr.hxx +++ b/simgear/props/PropertyInterpolationMgr.hxx @@ -51,10 +51,8 @@ public: PropertyInterpolationMgr(); - /** - * Update all active interpolators. - */ - void update(double dt); + // Subsystem API. + void update(double dt) override; /** * Create a new property interpolator. diff --git a/simgear/scene/tsync/terrasync.hxx b/simgear/scene/tsync/terrasync.hxx index 214fdf5a..89400f04 100644 --- a/simgear/scene/tsync/terrasync.hxx +++ b/simgear/scene/tsync/terrasync.hxx @@ -42,12 +42,13 @@ public: SGTerraSync(); virtual ~SGTerraSync(); - virtual void init(); - virtual void shutdown(); - virtual void reinit(); - virtual void bind(); - virtual void unbind(); - virtual void update(double); + // Subsystem API. + void bind() override; + void init() override; + void reinit() override; + void shutdown() override; + void unbind() override; + void update(double) override; /// notify terrasync that the sim was repositioned, as opposed to /// us travelling in a direction. Avoid last_lat / last_lon blocking diff --git a/simgear/sound/soundmgr.hxx b/simgear/sound/soundmgr.hxx index 55e683de..0d1ab95a 100644 --- a/simgear/sound/soundmgr.hxx +++ b/simgear/sound/soundmgr.hxx @@ -52,15 +52,18 @@ public: SGSoundMgr(); ~SGSoundMgr(); - void init(); - void update(double dt); + // Subsystem API. + void init() override; + void reinit() override; + void resume() override; + void suspend() override; + void update(double dt) override; + + // Subsystem identification. + static const char* subsystemName() { return "sound"; } - void suspend(); - void resume(); void stop(); - void reinit(); - /** * Select a specific sound device. * Requires a init/reinit call before sound is actually switched. @@ -323,8 +326,6 @@ public: bool testForError(std::string s, std::string name = "sound manager"); - static const char* subsystemName() { return "sound"; }; - private: class SoundManagerPrivate; /// private implementation object diff --git a/simgear/structure/SGPerfMon.hxx b/simgear/structure/SGPerfMon.hxx index 683beb46..14745cd2 100644 --- a/simgear/structure/SGPerfMon.hxx +++ b/simgear/structure/SGPerfMon.hxx @@ -37,10 +37,11 @@ class SGPerformanceMonitor : public SGSubsystem public: SGPerformanceMonitor(SGSubsystemMgr* subSysMgr, SGPropertyNode_ptr root); - virtual void bind (void); - virtual void unbind (void); - virtual void init (void); - virtual void update (double dt); + // Subsystem API. + void bind() override; + void init() override; + void unbind() override; + void update(double dt) override; private: static void subSystemMgrHook(void* userData, const std::string& name, SampleStatistic* timeStat); diff --git a/simgear/structure/event_mgr.hxx b/simgear/structure/event_mgr.hxx index ea79abcb..caf860b0 100644 --- a/simgear/structure/event_mgr.hxx +++ b/simgear/structure/event_mgr.hxx @@ -76,10 +76,11 @@ public: SGEventMgr(); ~SGEventMgr(); - virtual void init(); - virtual void update(double delta_time_sec); - virtual void unbind(); - virtual void shutdown(); + // Subsystem API. + void init() override; + void shutdown() override; + void unbind() override; + void update(double delta_time_sec) override; void setRealtimeProperty(SGPropertyNode* node) { _rtProp = node; } diff --git a/simgear/structure/subsystem_mgr.hxx b/simgear/structure/subsystem_mgr.hxx index 76b88327..a5f562d2 100644 --- a/simgear/structure/subsystem_mgr.hxx +++ b/simgear/structure/subsystem_mgr.hxx @@ -382,17 +382,18 @@ public: SGSubsystemGroup (const char *name); virtual ~SGSubsystemGroup (); + // Subsystem API. + void bind() override; + InitStatus incrementalInit() override; void init() override; - InitStatus incrementalInit () override; - void postinit () override; - void reinit () override; - void shutdown () override; - void bind () override; - void unbind () override; - void update (double delta_time_sec) override; - void suspend () override; - void resume () override; - bool is_suspended () const override; + void postinit() override; + void reinit() override; + void resume() override; + void shutdown() override; + void suspend() override; + void unbind() override; + void update(double delta_time_sec) override; + bool is_suspended() const override; virtual void set_subsystem (const std::string &name, SGSubsystem * subsystem, @@ -506,17 +507,18 @@ public: SGSubsystemMgr (const char *name); virtual ~SGSubsystemMgr (); - void init () override; - InitStatus incrementalInit () override; - void postinit () override; - void reinit () override; - void shutdown () override; - void bind () override; - void unbind () override; - void update (double delta_time_sec) override; - void suspend () override; - void resume () override; - bool is_suspended () const override; + // Subsystem API. + void bind() override; + void init() override; + InitStatus incrementalInit() override; + void postinit() override; + void reinit() override; + void resume() override; + void shutdown() override; + void suspend() override; + void unbind() override; + void update(double delta_time_sec) override; + bool is_suspended() const override; virtual void add (const char * name, SGSubsystem * subsystem, From d7323f5f190ead4bdcda567eb1db7faef2ac219f Mon Sep 17 00:00:00 2001 From: Edward d'Auvergne Date: Fri, 4 May 2018 14:16:42 +0200 Subject: [PATCH 34/39] SGSubsystem classes: Renaming of the subsystem ID variables and functions. --- simgear/sound/soundmgr.hxx | 2 +- simgear/structure/event_mgr.cxx | 1 - simgear/structure/subsystem_mgr.cxx | 58 ++++++++++++++-------------- simgear/structure/subsystem_mgr.hxx | 49 ++++++++++++----------- simgear/structure/subsystem_test.cxx | 41 ++++++++++---------- 5 files changed, 78 insertions(+), 73 deletions(-) diff --git a/simgear/sound/soundmgr.hxx b/simgear/sound/soundmgr.hxx index 0d1ab95a..6837a35f 100644 --- a/simgear/sound/soundmgr.hxx +++ b/simgear/sound/soundmgr.hxx @@ -60,7 +60,7 @@ public: void update(double dt) override; // Subsystem identification. - static const char* subsystemName() { return "sound"; } + static const char* staticSubsystemClassId() { return "sound"; } void stop(); diff --git a/simgear/structure/event_mgr.cxx b/simgear/structure/event_mgr.cxx index dead48b7..9d0e0ae0 100644 --- a/simgear/structure/event_mgr.cxx +++ b/simgear/structure/event_mgr.cxx @@ -47,7 +47,6 @@ SGEventMgr::SGEventMgr() : _inited(false), _shutdown(false) { -_name = "EventMgr"; } SGEventMgr::~SGEventMgr() diff --git a/simgear/structure/subsystem_mgr.cxx b/simgear/structure/subsystem_mgr.cxx index 92daefcf..6efcb2ad 100644 --- a/simgear/structure/subsystem_mgr.cxx +++ b/simgear/structure/subsystem_mgr.cxx @@ -138,28 +138,28 @@ void SGSubsystem::stamp(const string& name) void SGSubsystem::set_name(const std::string &n) { - assert(_name.empty()); - _name = n; + assert(_subsystemId.empty()); + _subsystemId = n; } -std::string SGSubsystem::typeName() const +std::string SGSubsystem::subsystemClassId() const { - auto pos = _name.find(SUBSYSTEM_NAME_SEPARATOR); + auto pos = _subsystemId.find(SUBSYSTEM_NAME_SEPARATOR); if (pos == std::string::npos) { - return _name; + return _subsystemId; } - return _name.substr(0, pos); + return _subsystemId.substr(0, pos); } -std::string SGSubsystem::instanceName() const +std::string SGSubsystem::subsystemInstanceId() const { - auto pos = _name.find(SUBSYSTEM_NAME_SEPARATOR); + auto pos = _subsystemId.find(SUBSYSTEM_NAME_SEPARATOR); if (pos == std::string::npos) { return {}; } - return _name.substr(pos+1); + return _subsystemId.substr(pos+1); } void SGSubsystem::set_group(SGSubsystemGroup* group) @@ -175,7 +175,7 @@ SGSubsystemGroup* SGSubsystem::get_group() const SGSubsystemMgr* SGSubsystem::get_manager() const { if (!get_group()) - throw sg_exception("SGSubsystem::get_manager: subsystem " + name() + " has no group"); + throw sg_exception("SGSubsystem::get_manager: subsystem " + subsystemId() + " has no group"); return get_group()->get_manager(); } @@ -474,7 +474,7 @@ void SGSubsystem::reportTimingStats(TimerStats *__lastValues) { if (reportDeltas) { auto deltaT = _executionTime - _lastExecutionTime; if (deltaT != 0) { - t << name() << "(+" << std::setprecision(2) << std::right << deltaT << "ms)."; + t << subsystemInstanceId() << "(+" << std::setprecision(2) << std::right << deltaT << "ms)."; _name = t.str(); } } @@ -506,7 +506,7 @@ void SGSubsystem::reportTimingStats(TimerStats *__lastValues) { void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) { SGSubsystem::reportTimingStats(_lastValues); - std::string _name = name(); + std::string _name = subsystemInstanceId(); if (!_name.size()) _name = typeid(this).name(); if (_lastValues) { @@ -514,11 +514,11 @@ void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) { if (deltaT != 0) { SG_LOG(SG_EVENT, SG_ALERT, " +" << std::setw(6) << std::setprecision(4) << std::right << deltaT << "ms " - << name() ); + << subsystemInstanceId() ); } } else - SG_LOG(SG_EVENT, SG_ALERT, "SubSystemGroup: " << name() << " " << std::setw(6) << std::setprecision(4) << std::right << _executionTime / 1000.0 << "s"); + SG_LOG(SG_EVENT, SG_ALERT, "SubSystemGroup: " << subsystemInstanceId() << " " << std::setw(6) << std::setprecision(4) << std::right << _executionTime / 1000.0 << "s"); for (auto member : _members) { member->reportTimingStats(_lastValues); } @@ -575,11 +575,11 @@ SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem, if (name.empty()) { SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group without a name"); // TODO, make this an exception in the future - } else if (subsystem->name().empty()) { + } else if (subsystem->subsystemId().empty()) { subsystem->set_name(name); - } else if (name != subsystem->name()) { + } else if (name != subsystem->subsystemId()) { SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group with name '" << name - << "', but name() returns '" << subsystem->name() << "'"); + << "', but subsystemId() returns '" << subsystem->subsystemId() << "'"); } notifyWillChange(subsystem, State::ADD); @@ -615,7 +615,7 @@ SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem, void SGSubsystemGroup::set_subsystem (SGSubsystem * subsystem, double min_step_sec) { - set_subsystem(subsystem->name(), subsystem, min_step_sec); + set_subsystem(subsystem->subsystemId(), subsystem, min_step_sec); } SGSubsystem * @@ -1039,9 +1039,9 @@ SGSubsystemMgr::get_subsystem (const string &name) const } SGSubsystem* -SGSubsystemMgr::get_subsystem(const std::string &name, const std::string& instanceName) const +SGSubsystemMgr::get_subsystem(const std::string &name, const std::string& subsystemInstanceId) const { - return get_subsystem(name + SUBSYSTEM_NAME_SEPARATOR + instanceName); + return get_subsystem(name + SUBSYSTEM_NAME_SEPARATOR + subsystemInstanceId); } @@ -1174,7 +1174,7 @@ SGSubsystemMgr::create(const std::string& name) } SGSubsystemRef -SGSubsystemMgr::createInstance(const std::string& name, const std::string& instanceName) +SGSubsystemMgr::createInstance(const std::string& name, const std::string& subsystemInstanceId) { auto it = findRegistration(name); auto &global_registrations = getGlobalRegistrations(); @@ -1191,7 +1191,7 @@ SGSubsystemMgr::createInstance(const std::string& name, const std::string& insta throw sg_exception("SGSubsystemMgr::create: functor failed to create an instsance of " + name); } - const auto combinedName = name + SUBSYSTEM_NAME_SEPARATOR + instanceName; + const auto combinedName = name + SUBSYSTEM_NAME_SEPARATOR + subsystemInstanceId; ref->set_name(combinedName); return ref; } @@ -1276,7 +1276,7 @@ namespace { // allow override of the name but defaultt o the subsystem name std::string name = arg->getStringValue("name"); - std::string instanceName = arg->getStringValue("instance"); + std::string subsystemInstanceId = arg->getStringValue("instance"); if (name.empty()) { // default name to subsystem name, but before we parse any instance name @@ -1285,19 +1285,19 @@ namespace { auto separatorPos = subsystem.find(SUBSYSTEM_NAME_SEPARATOR); if (separatorPos != std::string::npos) { - if (!instanceName.empty()) { + if (!subsystemInstanceId.empty()) { SG_LOG(SG_GENERAL, SG_WARN, "Specified a composite subsystem name and an instance name, please do one or the other: " - << instanceName << " and " << subsystem); + << subsystemInstanceId << " and " << subsystem); return false; } - instanceName = subsystem.substr(separatorPos + 1); + subsystemInstanceId = subsystem.substr(separatorPos + 1); subsystem = subsystem.substr(0, separatorPos); } SGSubsystem* sub = nullptr; - if (!instanceName.empty()) { - sub = manager->createInstance(subsystem, instanceName); + if (!subsystemInstanceId.empty()) { + sub = manager->createInstance(subsystem, subsystemInstanceId); } else { sub = manager->create(subsystem); } @@ -1313,7 +1313,7 @@ namespace { double minTime = arg->getDoubleValue("min-time-sec", 0.0); - const auto combinedName = subsystem + SUBSYSTEM_NAME_SEPARATOR + instanceName; + const auto combinedName = subsystem + SUBSYSTEM_NAME_SEPARATOR + subsystemInstanceId; manager->add(combinedName.c_str(), sub, group, diff --git a/simgear/structure/subsystem_mgr.hxx b/simgear/structure/subsystem_mgr.hxx index a5f562d2..fc52ded9 100644 --- a/simgear/structure/subsystem_mgr.hxx +++ b/simgear/structure/subsystem_mgr.hxx @@ -282,19 +282,19 @@ public: /** * composite name for this subsystem (type name & optional instance name) */ - std::string name() const - { return _name; } + std::string subsystemId() const + { return _subsystemId; } /** * @brief the type (class)-specific part of the subsystem name. */ - std::string typeName() const; + std::string subsystemClassId() const; /** * @brief the instance part of the subsystem name. Empty if this * subsystem is not instanced */ - std::string instanceName() const; + std::string subsystemInstanceId() const; virtual bool is_group() const { return false; } @@ -352,7 +352,7 @@ protected: /// is an instanced subsystem. (Since this member was originally defined as /// protected, not private, we can't rename it easily) std::string _name; - + bool _suspended = false; eventTimeVec timingInfo; @@ -363,6 +363,11 @@ protected: static int maxTimePerFrame_ms; private: + /// composite name for the subsystem (type name and instance name if this + /// is an instanced subsystem. (Since this member was originally defined as + /// protected, not private, we can't rename it easily) + std::string _subsystemId; + SGSubsystemGroup* _group = nullptr; protected: TimerStats _timerStats, _lastTimerStats; @@ -426,7 +431,7 @@ public: template T* get_subsystem() { - return dynamic_cast(get_subsystem(T::subsystemName())); + return dynamic_cast(get_subsystem(T::staticSubsystemClassId())); } bool is_group() const override @@ -535,7 +540,7 @@ public: SGSubsystem* get_subsystem(const std::string &name) const; - SGSubsystem* get_subsystem(const std::string &name, const std::string& instanceName) const; + SGSubsystem* get_subsystem(const std::string &name, const std::string& subsystemInstanceId) const; void reportTiming(); void setReportTimingCb(void* userData, SGSubsystemTimingCb cb) { reportTimingCb = cb; reportTimingUserData = userData; } @@ -556,21 +561,21 @@ public: template T* get_subsystem() const { - return dynamic_cast(get_subsystem(T::subsystemName())); + return dynamic_cast(get_subsystem(T::staticSubsystemClassId())); } // instanced overloads, for both raw char* and std::string // these concatenate the subsystem type name with the instance name template - T* get_subsystem(const char* instanceName) const + T* get_subsystem(const char* subsystemInstanceId) const { - return dynamic_cast(get_subsystem(T::subsystemName(), instanceName)); + return dynamic_cast(get_subsystem(T::staticSubsystemClassId(), subsystemInstanceId)); } template - T* get_subsystem(const std::string& instanceName) const + T* get_subsystem(const std::string& subsystemInstanceId) const { - return dynamic_cast(get_subsystem(T::subsystemName(), instanceName)); + return dynamic_cast(get_subsystem(T::staticSubsystemClassId(), subsystemInstanceId)); } struct Dependency { @@ -606,7 +611,7 @@ public: std::initializer_list deps = {}) { SubsystemFactoryFunctor factory = [](){ return new T; }; - SGSubsystemMgr::registerSubsystem(T::subsystemName(), + SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(), factory, group, false, updateInterval, deps); @@ -624,7 +629,7 @@ public: std::initializer_list deps = {}) { SubsystemFactoryFunctor factory = [](){ return new T; }; - SGSubsystemMgr::registerSubsystem(T::subsystemName(), + SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(), factory, group, true, updateInterval, deps); @@ -640,14 +645,14 @@ public: template SGSharedPtr add(GroupType customGroup = INVALID, double customInterval = 0.0) { - auto ref = create(T::subsystemName()); + auto ref = create(T::staticSubsystemClassId()); const GroupType group = (customGroup == INVALID) ? - defaultGroupFor(T::subsystemName()) : customGroup; + defaultGroupFor(T::staticSubsystemClassId()) : customGroup; const double interval = (customInterval == 0.0) ? - defaultUpdateIntervalFor(T::subsystemName()) : customInterval; - add(ref->name().c_str(), ref.ptr(), group, interval); + defaultUpdateIntervalFor(T::staticSubsystemClassId()) : customInterval; + add(ref->subsystemId().c_str(), ref.ptr(), group, interval); return dynamic_cast(ref.ptr()); } @@ -658,20 +663,20 @@ public: template SGSharedPtr create() { - auto ref = create(T::subsystemName()); + auto ref = create(T::staticSubsystemClassId()); return dynamic_cast(ref.ptr()); } SGSubsystemRef create(const std::string& name); template - SGSharedPtr createInstance(const std::string& instanceName) + SGSharedPtr createInstance(const std::string& subsystemInstanceId) { - auto ref = createInstance(T::subsystemName(), instanceName); + auto ref = createInstance(T::staticSubsystemClassId(), subsystemInstanceId); return dynamic_cast(ref.ptr()); } - SGSubsystemRef createInstance(const std::string& name, const std::string& instanceName); + SGSubsystemRef createInstance(const std::string& name, const std::string& subsystemInstanceId); static GroupType defaultGroupFor(const char* name); static double defaultUpdateIntervalFor(const char* name); diff --git a/simgear/structure/subsystem_test.cxx b/simgear/structure/subsystem_test.cxx index bdd0a6b5..c75149ee 100644 --- a/simgear/structure/subsystem_test.cxx +++ b/simgear/structure/subsystem_test.cxx @@ -20,7 +20,7 @@ using std::endl; class MySub1 : public SGSubsystem { public: - static const char* subsystemName() { return "mysub"; } + static const char* staticSubsystemClassId() { return "mysub"; } void init() override { @@ -45,7 +45,7 @@ public: class AnotherSub : public SGSubsystem { public: - static const char* subsystemName() { return "anothersub"; } + static const char* staticSubsystemClassId() { return "anothersub"; } void init() override { @@ -70,7 +70,7 @@ public: class FakeRadioSub : public SGSubsystem { public: - static const char* subsystemName() { return "fake-radio"; } + static const char* staticSubsystemClassId() { return "fake-radio"; } void init() override { @@ -89,8 +89,9 @@ public: class InstrumentGroup : public SGSubsystemGroup { public: - static const char* subsystemName() { return "instruments"; } - InstrumentGroup() : SGSubsystemGroup(InstrumentGroup::subsystemName()) {} + InstrumentGroup() : SGSubsystemGroup(InstrumentGroup::staticSubsystemClassId()) {} + static const char* staticSubsystemClassId() { return "instruments"; } + virtual ~InstrumentGroup() { } @@ -119,11 +120,11 @@ class RecorderDelegate : public SGSubsystemMgr::Delegate public: void willChange(SGSubsystem* sub, SGSubsystem::State newState) override { - events.push_back({sub->name(), false, newState}); + events.push_back({sub->subsystemId(), false, newState}); } void didChange(SGSubsystem* sub, SGSubsystem::State newState) override { - events.push_back({sub->name(), true, newState}); + events.push_back({sub->subsystemId(), true, newState}); } struct Event { @@ -168,10 +169,10 @@ void testRegistrationAndCreation() auto anotherSub = manager->create(); SG_VERIFY(anotherSub); - SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName()); - SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub")); - SG_CHECK_EQUAL(anotherSub->typeName(), std::string("anothersub")); - SG_CHECK_EQUAL(anotherSub->instanceName(), std::string()); + SG_CHECK_EQUAL(anotherSub->subsystemId(), AnotherSub::staticSubsystemClassId()); + SG_CHECK_EQUAL(anotherSub->subsystemId(), std::string("anothersub")); + SG_CHECK_EQUAL(anotherSub->subsystemClassId(), std::string("anothersub")); + SG_CHECK_EQUAL(anotherSub->subsystemInstanceId(), std::string()); auto radio1 = manager->createInstance("nav1"); auto radio2 = manager->createInstance("nav2"); @@ -187,8 +188,8 @@ void testAddGetRemove() auto anotherSub = manager->add(); SG_VERIFY(anotherSub); - SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName()); - SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub")); + SG_CHECK_EQUAL(anotherSub->subsystemId(), AnotherSub::staticSubsystemClassId()); + SG_CHECK_EQUAL(anotherSub->subsystemId(), std::string("anothersub")); SG_VERIFY(d->hasEvent("anothersub-will-add")); SG_VERIFY(d->hasEvent("anothersub-did-add")); @@ -200,14 +201,14 @@ void testAddGetRemove() // manual create & add auto mySub = manager->create(); - manager->add(MySub1::subsystemName(), mySub.ptr(), SGSubsystemMgr::DISPLAY, 0.1234); + manager->add(MySub1::staticSubsystemClassId(), mySub.ptr(), SGSubsystemMgr::DISPLAY, 0.1234); SG_VERIFY(d->hasEvent("mysub-will-add")); SG_VERIFY(d->hasEvent("mysub-did-add")); SG_CHECK_EQUAL(manager->get_subsystem(), mySub); - bool ok = manager->remove(AnotherSub::subsystemName()); + bool ok = manager->remove(AnotherSub::staticSubsystemClassId()); SG_VERIFY(ok); SG_VERIFY(d->hasEvent("anothersub-will-remove")); SG_VERIFY(d->hasEvent("anothersub-did-remove")); @@ -219,7 +220,7 @@ void testAddGetRemove() // re-add of removed, and let's test overriding auto another2 = manager->add(SGSubsystemMgr::SOUND); - SG_CHECK_EQUAL(another2->name(), AnotherSub::subsystemName()); + SG_CHECK_EQUAL(another2->subsystemId(), AnotherSub::staticSubsystemClassId()); auto soundGroup = manager->get_group(SGSubsystemMgr::SOUND); SG_CHECK_EQUAL(soundGroup->get_subsystem("anothersub"), another2); @@ -238,10 +239,10 @@ void testSubGrouping() auto radio1 = manager->createInstance("nav1"); auto radio2 = manager->createInstance("nav2"); - SG_CHECK_EQUAL(radio1->name(), std::string("fake-radio.nav1")); - SG_CHECK_EQUAL(radio2->name(), std::string("fake-radio.nav2")); - SG_CHECK_EQUAL(radio1->typeName(), std::string("fake-radio")); - SG_CHECK_EQUAL(radio2->instanceName(), std::string("nav2")); + SG_CHECK_EQUAL(radio1->subsystemId(), std::string("fake-radio.nav1")); + SG_CHECK_EQUAL(radio2->subsystemId(), std::string("fake-radio.nav2")); + SG_CHECK_EQUAL(radio1->subsystemClassId(), std::string("fake-radio")); + SG_CHECK_EQUAL(radio2->subsystemInstanceId(), std::string("nav2")); instruments->set_subsystem(radio1); instruments->set_subsystem(radio2); From 991fafb839063c854fb6e783c1efbf32816f8f19 Mon Sep 17 00:00:00 2001 From: Edward d'Auvergne Date: Thu, 13 Jun 2019 17:56:39 +0200 Subject: [PATCH 35/39] SGSubsystemGroup: Removal of the subsystem group naming. This is a partial reversion of c1dd8faa29136b30272e26042d24f079d5ec2b7c and a complete reversion of caea68007e0ef217d29c8b7432a7c712936b3e56 and 846f024e91252657313585455ac8dbfc5dbe9657. These changes are incompatible with the subsystem class IDs. --- simgear/structure/subsystem_mgr.cxx | 7 +++---- simgear/structure/subsystem_mgr.hxx | 4 ++-- simgear/structure/subsystem_test.cxx | 17 ++++++++--------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/simgear/structure/subsystem_mgr.cxx b/simgear/structure/subsystem_mgr.cxx index 6efcb2ad..6ceff02e 100644 --- a/simgear/structure/subsystem_mgr.cxx +++ b/simgear/structure/subsystem_mgr.cxx @@ -233,12 +233,11 @@ public: -SGSubsystemGroup::SGSubsystemGroup(const char *name) : +SGSubsystemGroup::SGSubsystemGroup() : _fixedUpdateTime(-1.0), _updateTimeRemainder(0.0), _initPosition(-1) { - _name = name; } SGSubsystemGroup::~SGSubsystemGroup () @@ -838,7 +837,7 @@ namespace { } // end of anonymous namespace -SGSubsystemMgr::SGSubsystemMgr (const char *name) : +SGSubsystemMgr::SGSubsystemMgr () : _groups(MAX_GROUPS) { if (global_defaultSubsystemManager == nullptr) { @@ -855,7 +854,7 @@ SGSubsystemMgr::SGSubsystemMgr (const char *name) : #endif for (int i = 0; i < MAX_GROUPS; i++) { - auto g = new SGSubsystemGroup(name); + auto g = new SGSubsystemGroup(); g->set_manager(this); _groups[i].reset(g); } diff --git a/simgear/structure/subsystem_mgr.hxx b/simgear/structure/subsystem_mgr.hxx index fc52ded9..af91dd4d 100644 --- a/simgear/structure/subsystem_mgr.hxx +++ b/simgear/structure/subsystem_mgr.hxx @@ -384,7 +384,7 @@ typedef SGSharedPtr SGSubsystemRef; class SGSubsystemGroup : public SGSubsystem { public: - SGSubsystemGroup (const char *name); + SGSubsystemGroup (); virtual ~SGSubsystemGroup (); // Subsystem API. @@ -509,7 +509,7 @@ public: MAX_GROUPS }; - SGSubsystemMgr (const char *name); + SGSubsystemMgr (); virtual ~SGSubsystemMgr (); // Subsystem API. diff --git a/simgear/structure/subsystem_test.cxx b/simgear/structure/subsystem_test.cxx index c75149ee..266b7fdd 100644 --- a/simgear/structure/subsystem_test.cxx +++ b/simgear/structure/subsystem_test.cxx @@ -89,7 +89,6 @@ public: class InstrumentGroup : public SGSubsystemGroup { public: - InstrumentGroup() : SGSubsystemGroup(InstrumentGroup::staticSubsystemClassId()) {} static const char* staticSubsystemClassId() { return "instruments"; } virtual ~InstrumentGroup() @@ -165,7 +164,7 @@ SGSubsystemMgr::InstancedRegistrant registrant3(SGSubsystemMgr::PO void testRegistrationAndCreation() { - SGSharedPtr manager = new SGSubsystemMgr("TEST1"); + SGSharedPtr manager = new SGSubsystemMgr(); auto anotherSub = manager->create(); SG_VERIFY(anotherSub); @@ -182,7 +181,7 @@ void testRegistrationAndCreation() void testAddGetRemove() { - SGSharedPtr manager = new SGSubsystemMgr("TEST1"); + SGSharedPtr manager = new SGSubsystemMgr(); auto d = new RecorderDelegate; manager->addDelegate(d); @@ -229,7 +228,7 @@ void testAddGetRemove() void testSubGrouping() { - SGSharedPtr manager = new SGSubsystemMgr("TEST1"); + SGSharedPtr manager = new SGSubsystemMgr(); auto d = new RecorderDelegate; manager->addDelegate(d); @@ -300,7 +299,7 @@ void testSubGrouping() void testIncrementalInit() { - SGSharedPtr manager = new SGSubsystemMgr("TEST"); + SGSharedPtr manager = new SGSubsystemMgr(); auto d = new RecorderDelegate; manager->addDelegate(d); @@ -348,7 +347,7 @@ void testEmptyGroup() // https://sourceforge.net/p/flightgear/codetickets/2043/ // when an empty group is inited, we skipped setting the state - SGSharedPtr manager = new SGSubsystemMgr("TEST"); + SGSharedPtr manager = new SGSubsystemMgr(); auto d = new RecorderDelegate; manager->addDelegate(d); @@ -372,7 +371,7 @@ void testEmptyGroup() void testSuspendResume() { - SGSharedPtr manager = new SGSubsystemMgr("TEST"); + SGSharedPtr manager = new SGSubsystemMgr(); auto d = new RecorderDelegate; manager->addDelegate(d); @@ -442,7 +441,7 @@ void testSuspendResume() void testPropertyRoot() { - SGSharedPtr manager = new SGSubsystemMgr("TEST"); + SGSharedPtr manager = new SGSubsystemMgr(); SGPropertyNode_ptr props(new SGPropertyNode); manager->set_root_node(props); @@ -468,7 +467,7 @@ void testPropertyRoot() void testAddRemoveAfterInit() { - SGSharedPtr manager = new SGSubsystemMgr("TEST"); + SGSharedPtr manager = new SGSubsystemMgr(); auto d = new RecorderDelegate; manager->addDelegate(d); From c8625ce599a302711d319d1fb4248c00c15c0f51 Mon Sep 17 00:00:00 2001 From: Edward d'Auvergne Date: Fri, 4 May 2018 14:28:15 +0200 Subject: [PATCH 36/39] SGSubsystem classes: Addition of staticSubsystemClassId() to all subsystems. --- simgear/misc/interpolator.hxx | 3 +++ simgear/scene/tsync/terrasync.hxx | 3 +++ simgear/structure/SGPerfMon.hxx | 3 +++ simgear/structure/event_mgr.hxx | 3 +++ simgear/structure/subsystem_mgr.hxx | 3 +++ 5 files changed, 15 insertions(+) diff --git a/simgear/misc/interpolator.hxx b/simgear/misc/interpolator.hxx index 17f72474..df530ee8 100644 --- a/simgear/misc/interpolator.hxx +++ b/simgear/misc/interpolator.hxx @@ -38,6 +38,9 @@ public: // Subsystem API. void update(double delta_time_sec) override; + // Subsystem identification. + static const char* staticSubsystemClassId() { return "interpolator"; } + // Simple method that interpolates a double property value from // its current value (default of zero) to the specified target // over the specified time. diff --git a/simgear/scene/tsync/terrasync.hxx b/simgear/scene/tsync/terrasync.hxx index 89400f04..4d4c0e39 100644 --- a/simgear/scene/tsync/terrasync.hxx +++ b/simgear/scene/tsync/terrasync.hxx @@ -50,6 +50,9 @@ public: void unbind() override; void update(double) override; + // Subsystem identification. + static const char* staticSubsystemClassId() { return "terrasync"; } + /// notify terrasync that the sim was repositioned, as opposed to /// us travelling in a direction. Avoid last_lat / last_lon blocking /// certain tiles when we reposition. diff --git a/simgear/structure/SGPerfMon.hxx b/simgear/structure/SGPerfMon.hxx index 14745cd2..63354d4a 100644 --- a/simgear/structure/SGPerfMon.hxx +++ b/simgear/structure/SGPerfMon.hxx @@ -43,6 +43,9 @@ public: void unbind() override; void update(double dt) override; + // Subsystem identification. + static const char* staticSubsystemClassId() { return "performance-mon"; } + private: static void subSystemMgrHook(void* userData, const std::string& name, SampleStatistic* timeStat); diff --git a/simgear/structure/event_mgr.hxx b/simgear/structure/event_mgr.hxx index caf860b0..9873cf71 100644 --- a/simgear/structure/event_mgr.hxx +++ b/simgear/structure/event_mgr.hxx @@ -82,6 +82,9 @@ public: void unbind() override; void update(double delta_time_sec) override; + // Subsystem identification. + static const char* staticSubsystemClassId() { return "events"; } + void setRealtimeProperty(SGPropertyNode* node) { _rtProp = node; } /** diff --git a/simgear/structure/subsystem_mgr.hxx b/simgear/structure/subsystem_mgr.hxx index af91dd4d..938bdb23 100644 --- a/simgear/structure/subsystem_mgr.hxx +++ b/simgear/structure/subsystem_mgr.hxx @@ -525,6 +525,9 @@ public: void update(double delta_time_sec) override; bool is_suspended() const override; + // Subsystem identification. + static const char* staticSubsystemClassId() { return "subsystem-mgr"; } + virtual void add (const char * name, SGSubsystem * subsystem, GroupType group = GENERAL, From 5bf28d3f900fab3c57f10232e965c86022c20032 Mon Sep 17 00:00:00 2001 From: Edward d'Auvergne Date: Mon, 7 May 2018 21:04:29 +0200 Subject: [PATCH 37/39] SGSubsystemMgr: Expanded the dependency list types. All the dependency types are now documented. --- simgear/structure/subsystem_mgr.hxx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/simgear/structure/subsystem_mgr.hxx b/simgear/structure/subsystem_mgr.hxx index 938bdb23..708f2ee4 100644 --- a/simgear/structure/subsystem_mgr.hxx +++ b/simgear/structure/subsystem_mgr.hxx @@ -581,11 +581,17 @@ public: return dynamic_cast(get_subsystem(T::staticSubsystemClassId(), subsystemInstanceId)); } + /** + * @brief Subsystem dependency structure. + */ struct Dependency { enum Type { - HARD, - SOFT, - PROPERTY + HARD, ///< The subsystem cannot run without this subsystem dependency. + SOFT, ///< The subsystem uses this subsystem dependency, but can run without it. + SEQUENCE, ///< Used for ordering subsystem initialisation. + NONSUBSYSTEM_HARD, ///< The subsystem cannot run without this non-subsystem dependency. + NONSUBSYSTEM_SOFT, ///< The subsystem uses this non-subsystem dependency, but can run without it. + PROPERTY ///< The subsystem requires this property to exist to run. }; std::string name; From c11557316da8ac034c038da072e5879aee517577 Mon Sep 17 00:00:00 2001 From: Edward d'Auvergne Date: Tue, 8 May 2018 10:17:34 +0200 Subject: [PATCH 38/39] SGSubsystemMgr: Changed the Registrant class argument order. The updateInterval argument is used a lot less than the dependency list, so these have been swapped. That simplifies the registration code in the subsystem implementations as the updateInterval default of 0.0 no longer need to be supplied. --- simgear/structure/subsystem_mgr.hxx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/simgear/structure/subsystem_mgr.hxx b/simgear/structure/subsystem_mgr.hxx index 708f2ee4..a8e99f69 100644 --- a/simgear/structure/subsystem_mgr.hxx +++ b/simgear/structure/subsystem_mgr.hxx @@ -616,8 +616,9 @@ public: class Registrant { public: - Registrant(GroupType group = GENERAL, double updateInterval = 0.0, - std::initializer_list deps = {}) + Registrant(GroupType group = GENERAL, + std::initializer_list deps = {}, + double updateInterval = 0.0) { SubsystemFactoryFunctor factory = [](){ return new T; }; SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(), @@ -634,8 +635,8 @@ public: { public: InstancedRegistrant(GroupType group = GENERAL, - double updateInterval = 0.0, - std::initializer_list deps = {}) + std::initializer_list deps = {}, + double updateInterval = 0.0) { SubsystemFactoryFunctor factory = [](){ return new T; }; SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(), From fa754ecaf8f936e2334fd280e8a03c9bdbe6b0e4 Mon Sep 17 00:00:00 2001 From: Edward d'Auvergne Date: Mon, 7 May 2018 08:49:06 +0200 Subject: [PATCH 39/39] SGSubsystem classes: Registration of all subsystems. --- simgear/scene/tsync/terrasync.cxx | 6 ++++++ simgear/structure/SGPerfMon.cxx | 4 ++++ simgear/structure/event_mgr.cxx | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/simgear/scene/tsync/terrasync.cxx b/simgear/scene/tsync/terrasync.cxx index 69e381f2..a1715097 100644 --- a/simgear/scene/tsync/terrasync.cxx +++ b/simgear/scene/tsync/terrasync.cxx @@ -1115,3 +1115,9 @@ void SGTerraSync::reposition() { // stub, remove } + + +// Register the subsystem. +SGSubsystemMgr::Registrant registrantSGTerraSync( + SGSubsystemMgr::GENERAL, + {{"FGRenderer", SGSubsystemMgr::Dependency::NONSUBSYSTEM_HARD}}); diff --git a/simgear/structure/SGPerfMon.cxx b/simgear/structure/SGPerfMon.cxx index c4d7caed..da754cab 100644 --- a/simgear/structure/SGPerfMon.cxx +++ b/simgear/structure/SGPerfMon.cxx @@ -138,3 +138,7 @@ SGPerformanceMonitor::reportTiming(const string& name, SampleStatistic* timeStat timeStat->reset(); } + + +// Register the subsystem. +//SGSubsystemMgr::Registrant registrantSGPerformanceMonitor; diff --git a/simgear/structure/event_mgr.cxx b/simgear/structure/event_mgr.cxx index 9d0e0ae0..2160d6c3 100644 --- a/simgear/structure/event_mgr.cxx +++ b/simgear/structure/event_mgr.cxx @@ -126,6 +126,11 @@ void SGEventMgr::dump() _rtQueue.dump(); } +// Register the subsystem. +SGSubsystemMgr::Registrant registrantSGEventMgr( + SGSubsystemMgr::DISPLAY); + + //////////////////////////////////////////////////////////////////////// // SGTimerQueue // This is the priority queue implementation: