Destroy render scene & engine before Application is destroyed

Many render items/payloads contain smart pointers back to the
objects that added them to the scene, including entity and avatar
objects. Currently, those render items are destroyed when the
scene is destroyed very late in the application life-cycle.

There are rare crashes that can occur when these render items are
destroyed. Possibly, due to them referencing objects that have
already been destroyed via raw pointers. In an effort to
eliminate these crashes, we now destroy the scene earlier, within
Application::aboutToQuit() which is connected to the
QCoreApplication::aboutToQuit signal.  Also, we guard against null
scene pointer dereferences.  Any location that accesses the scene
off the main thread, now checks the validity of the scene pointer.
This commit is contained in:
Anthony Thibault 2016-10-25 15:28:37 -07:00
parent 2b2b55aab0
commit 23aa626755
8 changed files with 62 additions and 23 deletions

View file

@ -1576,6 +1576,12 @@ void Application::cleanupBeforeQuit() {
#endif
DependencyManager::destroy<OffscreenUi>();
// shutdown render engine
_main3DScene = nullptr;
_renderEngine = nullptr;
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
}
Application::~Application() {

View file

@ -36,6 +36,7 @@
#include "Application.h"
#include "Avatar.h"
#include "AvatarManager.h"
#include "InterfaceLogging.h"
#include "Menu.h"
#include "MyAvatar.h"
#include "SceneScriptingInterface.h"
@ -208,11 +209,15 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
auto rawRenderableAvatar = std::static_pointer_cast<Avatar>(newAvatar);
render::ScenePointer scene = qApp->getMain3DScene();
render::PendingChanges pendingChanges;
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
rawRenderableAvatar->addToScene(rawRenderableAvatar, scene, pendingChanges);
if (scene) {
render::PendingChanges pendingChanges;
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
rawRenderableAvatar->addToScene(rawRenderableAvatar, scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
} else {
qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown";
}
scene->enqueuePendingChanges(pendingChanges);
return newAvatar;
}

View file

@ -128,11 +128,15 @@ void EntityTreeRenderer::clear() {
// remove all entities from the scene
auto scene = _viewState->getMain3DScene();
render::PendingChanges pendingChanges;
foreach(auto entity, _entitiesInScene) {
entity->removeFromScene(entity, scene, pendingChanges);
if (scene) {
render::PendingChanges pendingChanges;
foreach(auto entity, _entitiesInScene) {
entity->removeFromScene(entity, scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
} else {
qCWarning(entitiesrenderer) << "EntitityTreeRenderer::clear(), Unexpected null scene, possibly during application shutdown";
}
scene->enqueuePendingChanges(pendingChanges);
_entitiesInScene.clear();
// reset the zone to the default (while we load the next scene)
@ -901,8 +905,12 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
auto entity = _entitiesInScene.take(entityID);
render::PendingChanges pendingChanges;
auto scene = _viewState->getMain3DScene();
entity->removeFromScene(entity, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
if (scene) {
entity->removeFromScene(entity, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
} else {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), Unexpected null scene, possibly during application shutdown";
}
}
}
@ -919,10 +927,14 @@ void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
// here's where we add the entity payload to the scene
render::PendingChanges pendingChanges;
auto scene = _viewState->getMain3DScene();
if (entity->addToScene(entity, scene, pendingChanges)) {
_entitiesInScene.insert(entity->getEntityItemID(), entity);
if (scene) {
if (entity->addToScene(entity, scene, pendingChanges)) {
_entitiesInScene.insert(entity->getEntityItemID(), entity);
}
scene->enqueuePendingChanges(pendingChanges);
} else {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::addEntityToScene(), Unexpected null scene, possibly during application shutdown";
}
scene->enqueuePendingChanges(pendingChanges);
}

View file

@ -15,6 +15,7 @@
#include <render/Scene.h>
#include <EntityItem.h>
#include "AbstractViewStateInterface.h"
#include "EntitiesRendererLogging.h"
// These or the icon "name" used by the render item status value, they correspond to the atlas texture used by the DrawItemStatus
@ -79,10 +80,14 @@ public:
render::PendingChanges pendingChanges;
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
pendingChanges.updateItem<RenderableEntityItemProxy>(_myItem, [](RenderableEntityItemProxy& data) {
});
if (scene) {
pendingChanges.updateItem<RenderableEntityItemProxy>(_myItem, [](RenderableEntityItemProxy& data) {
});
scene->enqueuePendingChanges(pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
} else {
qCWarning(entitiesrenderer) << "SimpleRenderableEntityItem::notifyChanged(), Unexpected null scene, possibly during application shutdown";
}
}
private:

View file

@ -248,9 +248,12 @@ void RenderableZoneEntityItem::notifyBoundChanged() {
}
render::PendingChanges pendingChanges;
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
if (scene) {
pendingChanges.updateItem<RenderableZoneEntityItemMeta>(_myMetaItem, [](RenderableZoneEntityItemMeta& data) {
});
pendingChanges.updateItem<RenderableZoneEntityItemMeta>(_myMetaItem, [](RenderableZoneEntityItemMeta& data) {
});
scene->enqueuePendingChanges(pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
} else {
qCWarning(entitiesrenderer) << "RenderableZoneEntityItem::notifyBoundChanged(), Unexpected null scene, possibly during application shutdown";
}
}

View file

@ -862,8 +862,12 @@ void Model::setURL(const QUrl& url) {
{
render::PendingChanges pendingChanges;
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
removeFromScene(scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
if (scene) {
removeFromScene(scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
} else {
qCWarning(renderutils) << "Model::setURL(), Unexpected null scene, possibly during application shutdown";
}
}
_needsReload = true;

View file

@ -48,6 +48,10 @@ Scene::Scene(glm::vec3 origin, float size) :
_items.push_back(Item()); // add the itemID #0 to nothing
}
Scene::~Scene() {
qDebug() << "Scene::~Scene()";
}
ItemID Scene::allocateID() {
// Just increment and return the proevious value initialized at 0
return _IDAllocator.fetch_add(1);

View file

@ -55,7 +55,7 @@ typedef std::queue<PendingChanges> PendingChangesQueue;
class Scene {
public:
Scene(glm::vec3 origin, float size);
~Scene() {}
~Scene();
// This call is thread safe, can be called from anywhere to allocate a new ID
ItemID allocateID();