From d52d0d96400c9babc8a52fffa2edf4f101d05d34 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Jun 2015 18:16:02 +0200 Subject: [PATCH] Use the WorkQueue to update skinning --- components/sceneutil/riggeometry.cpp | 124 ++++++++++++++++++++++++--- components/sceneutil/riggeometry.hpp | 19 ++++ 2 files changed, 133 insertions(+), 10 deletions(-) diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 2a67c6ce6..50e47e289 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -5,6 +5,8 @@ #include +#include + #include #include "skeleton.hpp" @@ -63,6 +65,8 @@ RigGeometry::RigGeometry() , mFirstFrame(true) , mBoundsFirstFrame(true) { + initWorkQueue(); + setCullCallback(new UpdateRigGeometry); setUpdateCallback(new UpdateRigBounds); setSupportsDisplayList(false); @@ -75,9 +79,21 @@ RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) , mFirstFrame(copy.mFirstFrame) , mBoundsFirstFrame(copy.mBoundsFirstFrame) { + initWorkQueue(); + setSourceGeometry(copy.mSourceGeometry); } +void RigGeometry::initWorkQueue() +{ + static int numCpu = OpenThreads::GetNumberOfProcessors(); + if (numCpu > 1) + { + static WorkQueue sWorkQueue(1); + mWorkQueue = &sWorkQueue; + } +} + void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) { mSourceGeometry = sourceGeometry; @@ -198,22 +214,31 @@ void accummulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& ma ptrresult[14] += ptr[14] * weight; } -void RigGeometry::update(osg::NodeVisitor* nv) +class UpdateSkinningWorkItem : public WorkItem { - if (!mSkeleton) +public: + UpdateSkinningWorkItem(RigGeometry* rig, const osg::Matrixf& geomToSkelMatrix, RigGeometry::BoneMatrixMap boneMatrices) + : mRig(rig) + , mGeomToSkelMatrix(geomToSkelMatrix) + , mBoneMatrices(boneMatrices) { - if (!initFromParentSkeleton(nv)) - return; } - if (!mSkeleton->getActive() && !mFirstFrame) - return; - mFirstFrame = false; + virtual void doWork() + { + mRig->updateSkinning(mGeomToSkelMatrix, mBoneMatrices); - mSkeleton->updateBoneMatrices(nv); + mTicket->signalDone(); + } - osg::Matrixf geomToSkel = getGeomToSkelMatrix(nv); +private: + RigGeometry* mRig; + osg::Matrixf mGeomToSkelMatrix; + RigGeometry::BoneMatrixMap mBoneMatrices; +}; +void RigGeometry::updateSkinning(const osg::Matrixf& geomToSkel, RigGeometry::BoneMatrixMap boneMatrices) +{ // skinning osg::Vec3Array* positionSrc = static_cast(mSourceGeometry->getVertexArray()); osg::Vec3Array* normalSrc = static_cast(mSourceGeometry->getNormalArray()); @@ -233,7 +258,7 @@ void RigGeometry::update(osg::NodeVisitor* nv) Bone* bone = weightIt->first.first; const osg::Matrix& invBindMatrix = weightIt->first.second; float weight = weightIt->second; - const osg::Matrixf& boneMatrix = bone->mMatrixInSkeletonSpace; + const osg::Matrixf& boneMatrix = boneMatrices.at(bone); accummulateMatrix(invBindMatrix, boneMatrix, weight, resultMat); } resultMat = resultMat * geomToSkel; @@ -250,6 +275,45 @@ void RigGeometry::update(osg::NodeVisitor* nv) normalDst->dirty(); } +void RigGeometry::update(osg::NodeVisitor* nv) +{ + if (!mSkeleton) + { + if (!initFromParentSkeleton(nv)) + return; + } + + if (!mSkeleton->getActive() && !mFirstFrame) + return; + mFirstFrame = false; + + mSkeleton->updateBoneMatrices(nv); + + BoneMatrixMap boneMatrices; + for (BoneSphereMap::const_iterator it = mBoneSphereMap.begin(); it != mBoneSphereMap.end(); ++it) + boneMatrices[it->first] = it->first->mMatrixInSkeletonSpace; + + if (mWorkQueue) + { + // shouldn't happen, unless the CullCallback was a false positive, i.e. the Drawable's parent wasn't culled, but the Drawable *is* culled + if (mWorkTicket) + mWorkTicket->waitTillDone(); + + osg::Matrixf geomToSkel = getGeomToSkelMatrix(nv); + + // actual skinning update moved to a background thread + WorkItem* item = new UpdateSkinningWorkItem(this, geomToSkel, boneMatrices); + // keep the work ticket so we can synchronize in drawImplementation() + mWorkTicket = item->getTicket(); + + mWorkQueue->addWorkItem(item); + } + else + { + updateSkinning(getGeomToSkelMatrix(nv), boneMatrices); + } +} + void RigGeometry::updateBounds(osg::NodeVisitor *nv) { if (!mSkeleton) @@ -279,6 +343,46 @@ void RigGeometry::updateBounds(osg::NodeVisitor *nv) getParent(i)->dirtyBound(); } +void RigGeometry::drawImplementation(osg::RenderInfo &renderInfo) const +{ + if (mWorkTicket) + { + mWorkTicket->waitTillDone(); + mWorkTicket = NULL; + } + osg::Geometry::drawImplementation(renderInfo); +} + +void RigGeometry::compileGLObjects(osg::RenderInfo &renderInfo) const +{ + if (mWorkTicket) + { + mWorkTicket->waitTillDone(); + mWorkTicket = NULL; + } + osg::Geometry::compileGLObjects(renderInfo); +} + +void RigGeometry::accept(osg::PrimitiveFunctor &pf) const +{ + if (mWorkTicket) + { + mWorkTicket->waitTillDone(); + mWorkTicket = NULL; + } + osg::Geometry::accept(pf); +} + +void RigGeometry::accept(osg::PrimitiveIndexFunctor &pf) const +{ + if (mWorkTicket) + { + mWorkTicket->waitTillDone(); + mWorkTicket = NULL; + } + osg::Geometry::accept(pf); +} + osg::Matrixf RigGeometry::getGeomToSkelMatrix(osg::NodeVisitor *nv) { osg::NodePath path; diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index bd7c586c4..4dd8e8013 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -7,12 +7,16 @@ namespace SceneUtil { + class WorkQueue; + class WorkTicket; + class Skeleton; class Bone; /// @brief Mesh skinning implementation. /// @note A RigGeometry may be attached directly to a Skeleton, or somewhere below a Skeleton. /// Note though that the RigGeometry ignores any transforms below the Skeleton, so the attachment point is not that important. + /// @note You must use a double buffering scheme for queuing the drawing of RigGeometries, see FrameSwitch, or set their DataVariance to DYNAMIC class RigGeometry : public osg::Geometry { public: @@ -41,10 +45,23 @@ namespace SceneUtil // Called automatically by our CullCallback void update(osg::NodeVisitor* nv); + // Called by the worker thread + typedef std::map BoneMatrixMap; + void updateSkinning(const osg::Matrixf& geomToSkelMatrix, BoneMatrixMap boneMatrices); + // Called automatically by our UpdateCallback void updateBounds(osg::NodeVisitor* nv); + // Overriding a bunch of Drawable methods to synchronize access to our vertex array + virtual void drawImplementation(osg::RenderInfo& renderInfo) const; + virtual void compileGLObjects(osg::RenderInfo& renderInfo) const; + virtual void accept(osg::PrimitiveFunctor& pf) const; + virtual void accept(osg::PrimitiveIndexFunctor& pf) const; + private: + mutable osg::ref_ptr mWorkTicket; + WorkQueue* mWorkQueue; + osg::ref_ptr mSourceGeometry; Skeleton* mSkeleton; @@ -70,6 +87,8 @@ namespace SceneUtil bool initFromParentSkeleton(osg::NodeVisitor* nv); osg::Matrixf getGeomToSkelMatrix(osg::NodeVisitor* nv); + + void initWorkQueue(); }; }