mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 17:24:24 +02:00
Address performance issues introduced with this PR.
* Prevent clusterMatrices from being invalidated and re-computed in each updateItem lambda. We do this by not setting _model->_needsUpdateClusterMatrices = true; * Prevent redundant work if Model::enqueueLocationChange is called multiple times per frame. We do this by introducing a preRenderLambdas map in the Application class. Instead of adding work directly to the scene PendingChanges queue Model::enqueueLocationChange adds a lambda to the Application preRenderLambdas map. The Application ensures that only one lambda will be invoked for each model per frame.
This commit is contained in:
parent
e77cf54483
commit
115fd607a0
5 changed files with 69 additions and 31 deletions
|
@ -2985,6 +2985,11 @@ void Application::updateLOD() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::pushPreRenderLambda(void* key, std::function<void()> func) {
|
||||||
|
std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
|
||||||
|
_preRenderLambdas[key] = func;
|
||||||
|
}
|
||||||
|
|
||||||
// Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.
|
// Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.
|
||||||
// (Maybe this code should be moved there?)
|
// (Maybe this code should be moved there?)
|
||||||
// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition().
|
// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition().
|
||||||
|
@ -3461,6 +3466,16 @@ void Application::update(float deltaTime) {
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "sendDownstreamAudioStatsPacket", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "sendDownstreamAudioStatsPacket", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0);
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
|
||||||
|
for (auto& iter : _preRenderLambdas) {
|
||||||
|
iter.second();
|
||||||
|
}
|
||||||
|
_preRenderLambdas.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -211,6 +211,8 @@ public:
|
||||||
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
||||||
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
||||||
|
|
||||||
|
virtual void pushPreRenderLambda(void* key, std::function<void()> func) override;
|
||||||
|
|
||||||
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
|
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
|
||||||
|
|
||||||
void updateMyAvatarLookAtPosition();
|
void updateMyAvatarLookAtPosition();
|
||||||
|
@ -510,6 +512,9 @@ private:
|
||||||
bool _cursorNeedsChanging { false };
|
bool _cursorNeedsChanging { false };
|
||||||
|
|
||||||
QThread* _deadlockWatchdogThread;
|
QThread* _deadlockWatchdogThread;
|
||||||
|
|
||||||
|
std::map<void*, std::function<void()>> _preRenderLambdas;
|
||||||
|
std::mutex _preRenderLambdasLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Application_h
|
#endif // hifi_Application_h
|
||||||
|
|
|
@ -46,6 +46,8 @@ public:
|
||||||
virtual render::ScenePointer getMain3DScene() = 0;
|
virtual render::ScenePointer getMain3DScene() = 0;
|
||||||
virtual render::EnginePointer getRenderEngine() = 0;
|
virtual render::EnginePointer getRenderEngine() = 0;
|
||||||
|
|
||||||
|
virtual void pushPreRenderLambda(void* key, std::function<void()> func) = 0;
|
||||||
|
|
||||||
// FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface
|
// FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface
|
||||||
static AbstractViewStateInterface* instance();
|
static AbstractViewStateInterface* instance();
|
||||||
static void setInstance(AbstractViewStateInterface* instance);
|
static void setInstance(AbstractViewStateInterface* instance);
|
||||||
|
|
|
@ -347,7 +347,7 @@ void ModelMeshPartPayload::initCache() {
|
||||||
|
|
||||||
|
|
||||||
void ModelMeshPartPayload::notifyLocationChanged() {
|
void ModelMeshPartPayload::notifyLocationChanged() {
|
||||||
_model->_needsUpdateClusterMatrices = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const glm::mat4* clusterMatrices, size_t numClusterMatrices) {
|
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const glm::mat4* clusterMatrices, size_t numClusterMatrices) {
|
||||||
|
|
|
@ -128,45 +128,61 @@ void Model::setOffset(const glm::vec3& offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::enqueueLocationChange() {
|
void Model::enqueueLocationChange() {
|
||||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
|
||||||
|
|
||||||
Transform modelTransform;
|
// queue up this work for later processing, at the end of update and just before rendering.
|
||||||
modelTransform.setScale(_scale);
|
// the application will ensure only the last lambda is actually invoked.
|
||||||
modelTransform.setTranslation(_translation);
|
void* key = (void*)this;
|
||||||
modelTransform.setRotation(_rotation);
|
std::weak_ptr<Model> weakSelf = shared_from_this();
|
||||||
|
AbstractViewStateInterface::instance()->pushPreRenderLambda(key, [weakSelf]() {
|
||||||
|
|
||||||
Transform modelMeshOffset;
|
// do nothing, if the model has already been destroyed.
|
||||||
if (_geometry && _geometry->isLoaded()) {
|
auto self = weakSelf.lock();
|
||||||
modelMeshOffset = Transform(_rig->getGeometryToRigTransform());
|
if (!self) {
|
||||||
} else {
|
return;
|
||||||
modelMeshOffset.postTranslate(_offset);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Transform collisionMeshOffset;
|
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||||
collisionMeshOffset.postTranslate(_offset);
|
|
||||||
|
|
||||||
|
Transform modelTransform;
|
||||||
|
modelTransform.setScale(self->_scale);
|
||||||
|
modelTransform.setTranslation(self->_translation);
|
||||||
|
modelTransform.setRotation(self->_rotation);
|
||||||
|
|
||||||
render::PendingChanges pendingChanges;
|
Transform modelMeshOffset;
|
||||||
foreach (auto itemID, _modelMeshRenderItems.keys()) {
|
if (self->_geometry && self->_geometry->isLoaded()) {
|
||||||
pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, modelMeshOffset](ModelMeshPartPayload& data) {
|
// includes model offset and unitScale.
|
||||||
|
modelMeshOffset = Transform(self->_rig->getGeometryToRigTransform());
|
||||||
|
} else {
|
||||||
|
modelMeshOffset.postTranslate(self->_offset);
|
||||||
|
}
|
||||||
|
|
||||||
data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation());
|
// only apply offset only, collision mesh does not share the same unit scale as the FBX file's mesh.
|
||||||
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex);
|
Transform collisionMeshOffset;
|
||||||
size_t numClusterMatrices = data._model->getGeometry()->getFBXGeometry().meshes.at(data._meshIndex).clusters.size();
|
collisionMeshOffset.postTranslate(self->_offset);
|
||||||
|
|
||||||
data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, &state.clusterMatrices[0], numClusterMatrices);
|
render::PendingChanges pendingChanges;
|
||||||
data.notifyLocationChanged();
|
foreach (auto itemID, self->_modelMeshRenderItems.keys()) {
|
||||||
});
|
pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, modelMeshOffset](ModelMeshPartPayload& data) {
|
||||||
}
|
|
||||||
|
|
||||||
foreach (auto itemID, _collisionRenderItems.keys()) {
|
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
|
||||||
pendingChanges.updateItem<MeshPartPayload>(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) {
|
data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation());
|
||||||
data.updateTransform(modelTransform, collisionMeshOffset);
|
|
||||||
data.notifyLocationChanged();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
// update the model transform and bounding box for this render item.
|
||||||
|
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex);
|
||||||
|
size_t numClusterMatrices = data._model->getGeometry()->getFBXGeometry().meshes.at(data._meshIndex).clusters.size();
|
||||||
|
data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, &state.clusterMatrices[0], numClusterMatrices);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (auto itemID, self->_collisionRenderItems.keys()) {
|
||||||
|
pendingChanges.updateItem<MeshPartPayload>(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) {
|
||||||
|
// update the model transform for this render item.
|
||||||
|
data.updateTransform(modelTransform, collisionMeshOffset);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::initJointTransforms() {
|
void Model::initJointTransforms() {
|
||||||
|
|
Loading…
Reference in a new issue