summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Parks <davep@lindenlab.com>2024-09-16 18:49:03 -0500
committerGitHub <noreply@github.com>2024-09-16 18:49:03 -0500
commit486613e79bb96b838121f627ef73b1293ee18c12 (patch)
tree0c7cc6b8dcac2f36b0d5f54e374e92a74b746be3
parent42975dfd8868454172ca0c3fcfa9ae18cb1d4de8 (diff)
Profile guided optimization pass (#2582)
- Tune up LLJointRiggingInfoTab - Visualize joint bounding boxes when visualizing joints - Use LLJointRiggingInfo to caclulate desired resolution of a texture - Throttle calls to calcPixelArea - Fetch MeshSkinInfo immediately when header is received
-rw-r--r--indra/llmath/llrigginginfo.h4
-rw-r--r--indra/llprimitive/llmodel.cpp7
-rw-r--r--indra/llprimitive/llmodel.h5
-rw-r--r--indra/llrender/llrendersphere.cpp62
-rw-r--r--indra/llrender/llrendersphere.h1
-rw-r--r--indra/llrender/llvertexbuffer.cpp2
-rw-r--r--indra/newview/llface.cpp79
-rw-r--r--indra/newview/llface.h13
-rw-r--r--indra/newview/llmeshrepository.cpp88
-rw-r--r--indra/newview/llmeshrepository.h19
-rw-r--r--indra/newview/llskinningutil.cpp43
-rw-r--r--indra/newview/llviewerobject.cpp1
-rw-r--r--indra/newview/llvoavatar.cpp116
-rw-r--r--indra/newview/llvoavatar.h2
-rw-r--r--indra/newview/llvovolume.cpp6
15 files changed, 345 insertions, 103 deletions
diff --git a/indra/llmath/llrigginginfo.h b/indra/llmath/llrigginginfo.h
index fb550d013f..d761af68b1 100644
--- a/indra/llmath/llrigginginfo.h
+++ b/indra/llmath/llrigginginfo.h
@@ -66,6 +66,10 @@ public:
const LLJointRiggingInfo& operator[](S32 i) const { return mRigInfoPtr[i]; };
bool needsUpdate() { return mNeedsUpdate; }
void setNeedsUpdate(bool val) { mNeedsUpdate = val; }
+
+ LLJointRiggingInfo* begin() { return mRigInfoPtr; }
+ LLJointRiggingInfo* end() { return mRigInfoPtr + mSize; }
+
private:
// Not implemented
LLJointRiggingInfoTab& operator=(const LLJointRiggingInfoTab& src);
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 15087a7255..9908a155f2 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -1547,6 +1547,13 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin)
mLockScaleIfJointPosition = false;
}
+ // combine mBindShapeMatrix and mInvBindMatrix into mBindPoseMatrix
+ mBindPoseMatrix.resize(mInvBindMatrix.size());
+ for (U32 i = 0; i < mInvBindMatrix.size(); ++i)
+ {
+ matMul(mBindShapeMatrix, mInvBindMatrix[i], mBindPoseMatrix[i]);
+ }
+
updateHash();
}
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index b0cba2d655..96cfb7151e 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -56,12 +56,15 @@ public:
LLUUID mMeshID;
std::vector<std::string> mJointNames;
mutable std::vector<S32> mJointNums;
- typedef std::vector<LLMatrix4a, boost::alignment::aligned_allocator<LLMatrix4a, 16>> matrix_list_t;
+ typedef std::vector<LLMatrix4a> matrix_list_t;
matrix_list_t mInvBindMatrix;
// bones/joints position overrides
matrix_list_t mAlternateBindMatrix;
+ // cached multiply of mBindShapeMatrix and mInvBindMatrix
+ matrix_list_t mBindPoseMatrix;
+
LL_ALIGN_16(LLMatrix4a mBindShapeMatrix);
float mPelvisOffset;
diff --git a/indra/llrender/llrendersphere.cpp b/indra/llrender/llrendersphere.cpp
index 9570180554..cd8ef7d68e 100644
--- a/indra/llrender/llrendersphere.cpp
+++ b/indra/llrender/llrendersphere.cpp
@@ -34,6 +34,8 @@
#include "llerror.h"
#include "llglheaders.h"
+#include "llvertexbuffer.h"
+#include "llglslshader.h"
LLRenderSphere gSphere;
@@ -53,12 +55,20 @@ inline LLVector3 polar_to_cart(F32 latitude, F32 longitude)
void LLRenderSphere::renderGGL()
{
+ LL_PROFILE_ZONE_SCOPED;
S32 const LATITUDE_SLICES = 20;
S32 const LONGITUDE_SLICES = 30;
- if (mSpherePoints.empty())
+ if (mVertexBuffer.isNull())
{
mSpherePoints.resize(LATITUDE_SLICES + 1);
+ mVertexBuffer = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX);
+
+ mVertexBuffer->allocateBuffer((U32)(LATITUDE_SLICES + 1) * (LONGITUDE_SLICES + 1), LATITUDE_SLICES * LONGITUDE_SLICES * 6);
+
+ LLStrider<LLVector3> v;
+ mVertexBuffer->getVertexStrider(v);
+
for (S32 lat_i = 0; lat_i < LATITUDE_SLICES + 1; lat_i++)
{
mSpherePoints[lat_i].resize(LONGITUDE_SLICES + 1);
@@ -68,24 +78,52 @@ void LLRenderSphere::renderGGL()
F32 lon = (F32)lon_i / LONGITUDE_SLICES;
mSpherePoints[lat_i][lon_i] = polar_to_cart(lat, lon);
+ v[lat_i * (LONGITUDE_SLICES + 1) + lon_i] = mSpherePoints[lat_i][lon_i];
}
}
+
+ LLStrider<U16> i;
+ mVertexBuffer->getIndexStrider(i);
+
+ for (S32 lat_i = 0; lat_i < LATITUDE_SLICES; lat_i++)
+ {
+ for (S32 lon_i = 0; lon_i < LONGITUDE_SLICES; lon_i++)
+ {
+ i[(lat_i * LONGITUDE_SLICES + lon_i) * 6 + 0] = lat_i * (LONGITUDE_SLICES + 1) + lon_i;
+ i[(lat_i * LONGITUDE_SLICES + lon_i) * 6 + 1] = lat_i * (LONGITUDE_SLICES + 1) + lon_i + 1;
+ i[(lat_i * LONGITUDE_SLICES + lon_i) * 6 + 2] = (lat_i + 1) * (LONGITUDE_SLICES + 1) + lon_i;
+
+ i[(lat_i * LONGITUDE_SLICES + lon_i) * 6 + 3] = (lat_i + 1) * (LONGITUDE_SLICES + 1) + lon_i;
+ i[(lat_i * LONGITUDE_SLICES + lon_i) * 6 + 4] = lat_i * (LONGITUDE_SLICES + 1) + lon_i + 1;
+ i[(lat_i * LONGITUDE_SLICES + lon_i) * 6 + 5] = (lat_i + 1) * (LONGITUDE_SLICES + 1) + lon_i + 1;
+ }
+ }
+
+ mVertexBuffer->unmapBuffer();
}
- gGL.begin(LLRender::TRIANGLES);
- for (S32 lat_i = 0; lat_i < LATITUDE_SLICES; lat_i++)
- {
- for (S32 lon_i = 0; lon_i < LONGITUDE_SLICES; lon_i++)
+ if (LLGLSLShader::sCurBoundShaderPtr->mAttributeMask == LLVertexBuffer::MAP_VERTEX)
+ { // shader expects only vertex positions in vertex buffer, use fast path
+ mVertexBuffer->setBuffer();
+ mVertexBuffer->drawRange(LLRender::TRIANGLES, 0, mVertexBuffer->getNumVerts(), mVertexBuffer->getNumIndices(), 0);
+ }
+ else
+ { //shader wants colors in the vertex stream, use slow path
+ gGL.begin(LLRender::TRIANGLES);
+ for (S32 lat_i = 0; lat_i < LATITUDE_SLICES; lat_i++)
{
- gGL.vertex3fv(mSpherePoints[lat_i][lon_i].mV);
- gGL.vertex3fv(mSpherePoints[lat_i][lon_i+1].mV);
- gGL.vertex3fv(mSpherePoints[lat_i+1][lon_i].mV);
+ for (S32 lon_i = 0; lon_i < LONGITUDE_SLICES; lon_i++)
+ {
+ gGL.vertex3fv(mSpherePoints[lat_i][lon_i].mV);
+ gGL.vertex3fv(mSpherePoints[lat_i][lon_i + 1].mV);
+ gGL.vertex3fv(mSpherePoints[lat_i + 1][lon_i].mV);
- gGL.vertex3fv(mSpherePoints[lat_i+1][lon_i].mV);
- gGL.vertex3fv(mSpherePoints[lat_i][lon_i+1].mV);
- gGL.vertex3fv(mSpherePoints[lat_i+1][lon_i+1].mV);
+ gGL.vertex3fv(mSpherePoints[lat_i + 1][lon_i].mV);
+ gGL.vertex3fv(mSpherePoints[lat_i][lon_i + 1].mV);
+ gGL.vertex3fv(mSpherePoints[lat_i + 1][lon_i + 1].mV);
+ }
}
+ gGL.end();
}
- gGL.end();
}
diff --git a/indra/llrender/llrendersphere.h b/indra/llrender/llrendersphere.h
index e2e886fa06..5b6eabecb8 100644
--- a/indra/llrender/llrendersphere.h
+++ b/indra/llrender/llrendersphere.h
@@ -45,6 +45,7 @@ public:
private:
std::vector< std::vector<LLVector3> > mSpherePoints;
+ LLPointer<LLVertexBuffer> mVertexBuffer;
};
extern LLRenderSphere gSphere;
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index 3a18c28a62..e9fa369b0c 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -1341,8 +1341,6 @@ void LLVertexBuffer::unmapBuffer()
void LLVertexBuffer::_mapBuffer()
{
- // must only be called from main thread
- llassert(LLCoros::on_main_thread_main_coro());
if (!mMapped)
{
mMapped = true;
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 48fddc32db..ce68474211 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -56,6 +56,7 @@
#include "llvoavatar.h"
#include "llsculptidsize.h"
#include "llmeshrepository.h"
+#include "llskinningutil.h"
#if LL_LINUX
// Work-around spurious used before init warning on Vector4a
@@ -2184,28 +2185,88 @@ F32 LLFace::getTextureVirtualSize()
bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
{
+ constexpr F32 PIXEL_AREA_UPDATE_PERIOD = 0.1f;
+ // this is an expensive operation and the result is valid (enough) for several frames
+ // don't update every frame
+ if (gFrameTimeSeconds - mLastPixelAreaUpdate < PIXEL_AREA_UPDATE_PERIOD)
+ {
+ return true;
+ }
+
LL_PROFILE_ZONE_SCOPED_CATEGORY_FACE;
//get area of circle around face
-
LLVector4a center;
LLVector4a size;
-
if (isState(LLFace::RIGGED))
{
- //override with avatar bounding box
+ LL_PROFILE_ZONE_NAMED_CATEGORY_FACE("calcPixelArea - rigged");
+ //override with joint volume face joint bounding boxes
LLVOAvatar* avatar = mVObjp->getAvatar();
+ bool hasRiggedExtents = false;
+
if (avatar && avatar->mDrawable)
{
- center.load3(avatar->getPositionAgent().mV);
- const LLVector4a* exts = avatar->mDrawable->getSpatialExtents();
- size.setSub(exts[1], exts[0]);
+ LLVolume* volume = mVObjp->getVolume();
+ if (volume)
+ {
+ LLVolumeFace& face = volume->getVolumeFace(mTEOffset);
+
+ auto& rigInfo = face.mJointRiggingInfoTab;
+
+ if (rigInfo.needsUpdate())
+ {
+ LLVOVolume* vo_volume = (LLVOVolume*)mVObjp.get();
+ LLVOAvatar* avatar = mVObjp->getAvatar();
+ const LLMeshSkinInfo* skin = vo_volume->getSkinInfo();
+ LLSkinningUtil::updateRiggingInfo(skin, avatar, face);
+ }
+
+ // calculate the world space bounding box of the face by combining the bounding boxes of all the joints
+ LLVector4a& minp = mRiggedExtents[0];
+ LLVector4a& maxp = mRiggedExtents[1];
+ minp = LLVector4a(FLT_MAX, FLT_MAX, FLT_MAX);
+ maxp = LLVector4a(-FLT_MAX, -FLT_MAX, -FLT_MAX);
+
+ for (S32 i = 0; i < rigInfo.size(); i++)
+ {
+ auto& jointInfo = rigInfo[i];
+ if (jointInfo.isRiggedTo())
+ {
+ LLJoint* joint = avatar->getJoint(i);
+
+ if (joint)
+ {
+ LLVector4a jointPos;
+
+ LLMatrix4a worldMat;
+ worldMat.loadu((F32*)&joint->getWorldMatrix().mMatrix[0][0]);
+
+ LLVector4a extents[2];
+
+ matMulBoundBox(worldMat, jointInfo.getRiggedExtents(), extents);
+
+ minp.setMin(minp, extents[0]);
+ maxp.setMax(maxp, extents[1]);
+ hasRiggedExtents = true;
+ }
+ }
+ }
+ }
}
- else
+
+ if (!hasRiggedExtents)
{
+ // no rigged extents, zero out bounding box and skip update
+ mRiggedExtents[0] = mRiggedExtents[1] = LLVector4a(0.f, 0.f, 0.f);
+
return false;
}
+
+ center.setAdd(mRiggedExtents[1], mRiggedExtents[0]);
+ center.mul(0.5f);
+ size.setSub(mRiggedExtents[1], mRiggedExtents[0]);
}
else
{
@@ -2231,6 +2292,10 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
F32 app_angle = atanf((F32) sqrt(size_squared) / dist);
radius = app_angle*LLDrawable::sCurPixelAngle;
mPixelArea = radius*radius * 3.14159f;
+
+ // remember last update time, add 10% noise to avoid all faces updating at the same time
+ mLastPixelAreaUpdate = gFrameTimeSeconds + ll_frand() * PIXEL_AREA_UPDATE_PERIOD * 0.1f;
+
LLVector4a x_axis;
x_axis.load3(camera->getXAxis().mV);
cos_angle_to_view_dir = lookAt.dot3(x_axis).getF32();
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 99642016f7..65637fbf85 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -234,8 +234,14 @@ public:
// return true if this face is in an alpha draw pool
bool isInAlphaPool() const;
public: //aligned members
+
+ // bounding box of face in drawable space
LLVector4a mExtents[2];
+ // cached bounding box of rigged face in world space
+ // calculated on-demand by LLFace::calcPixelArea and may not be up-to-date
+ LLVector4a mRiggedExtents[2] = { LLVector4a(0,0,0), LLVector4a(0,0,0) };
+
private:
friend class LLViewerTextureList;
F32 adjustPartialOverlapPixelArea(F32 cos_angle_to_view_dir, F32 radius );
@@ -301,7 +307,14 @@ private:
S32 mReferenceIndex;
std::vector<S32> mRiggedIndex;
+ // gFrameTimeSeconds when mPixelArea was last updated
+ F32 mLastPixelAreaUpdate = 0.f;
+
+ // virtual size of face in texture area (mPixelArea adjusted by texture repeats)
+ // used to determine desired resolution of texture
F32 mVSize;
+
+ // pixel area face covers on screen
F32 mPixelArea;
//importance factor, in the range [0, 1.0].
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 26e2d8f319..532a87bbd1 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -77,6 +77,8 @@
#include "llinventorypanel.h"
#include "lluploaddialog.h"
#include "llfloaterreg.h"
+#include "llvoavatarself.h"
+#include "llskinningutil.h"
#include "boost/iostreams/device/array.hpp"
#include "boost/iostreams/stream.hpp"
@@ -757,7 +759,7 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
<< " (" << status.toTerseString() << ")" << LL_ENDL;
std::ostringstream details;
- typedef std::set<std::string> mav_errors_set_t;
+ typedef std::unordered_set<std::string> mav_errors_set_t;
mav_errors_set_t mav_errors;
if (content.has("error"))
@@ -828,7 +830,8 @@ LLMeshRepoThread::LLMeshRepoThread()
mHttpLargeOptions(),
mHttpHeaders(),
mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
- mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID)
+ mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
+ mWorkQueue("MeshRepoThread", 1024*1024)
{
LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp());
@@ -911,6 +914,10 @@ void LLMeshRepoThread::run()
break;
}
+ // run mWorkQueue for up to 8ms
+ static std::chrono::nanoseconds WorkTimeNanoSec{std::chrono::nanoseconds::rep(8 * 1000000) };
+ mWorkQueue.runFor(WorkTimeNanoSec);
+
if (! mHttpRequestSet.empty())
{
// Dispatch all HttpHandler notifications
@@ -1322,7 +1329,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url,
bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
{
-
+ LL_PROFILE_ZONE_SCOPED;
if (!mHeaderMutex)
{
return false;
@@ -1447,6 +1454,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
{
+ LL_PROFILE_ZONE_SCOPED;
if (!mHeaderMutex)
{
return false;
@@ -1554,6 +1562,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
{
+ LL_PROFILE_ZONE_SCOPED;
if (!mHeaderMutex)
{
return false;
@@ -1693,6 +1702,7 @@ void LLMeshRepoThread::decActiveHeaderRequests()
//return false if failed to get header
bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry)
{
+ LL_PROFILE_ZONE_SCOPED;
++LLMeshRepository::sMeshRequestCount;
{
@@ -1756,6 +1766,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c
//return false if failed to get mesh lod.
bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry)
{
+ LL_PROFILE_ZONE_SCOPED;
if (!mHeaderMutex)
{
return false;
@@ -1940,6 +1951,18 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes
LLMeshRepository::sCacheBytesHeaders += (U32)header_size;
}
+ // immediately request SkinInfo since we'll need it before we can render any LoD if it is present
+ {
+ LLMutexLock lock(gMeshRepo.mMeshMutex);
+
+ if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end())
+ {
+ gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info
+ }
+ }
+
+ fetchMeshSkinInfo(mesh_id);
+
LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time.
//check for pending requests
@@ -1971,6 +1994,18 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
{
if (volume->getNumFaces() > 0)
{
+ // if we have a valid SkinInfo, cache per-joint bounding boxes for this LOD
+ LLMeshSkinInfo* skin_info = mSkinMap[mesh_params.getSculptID()];
+ if (skin_info && isAgentAvatarValid())
+ {
+ for (S32 i = 0; i < volume->getNumFaces(); ++i)
+ {
+ // NOTE: no need to lock gAgentAvatarp as the state being checked is not changed after initialization
+ LLVolumeFace& face = volume->getVolumeFace(i);
+ LLSkinningUtil::updateRiggingInfo(skin_info, gAgentAvatarp, face);
+ }
+ }
+
LoadedMesh mesh(volume, mesh_params, lod);
{
LLMutexLock lock(mMutex);
@@ -2014,18 +2049,19 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
}
{
- LLMeshSkinInfo* info = nullptr;
- try
- {
- info = new LLMeshSkinInfo(mesh_id, skin);
- }
- catch (const std::bad_alloc& ex)
- {
- LL_WARNS() << "Failed to allocate skin info with exception: " << ex.what() << LL_ENDL;
- return false;
+ LLPointer<LLMeshSkinInfo> info = nullptr;
+ info = new LLMeshSkinInfo(mesh_id, skin);
+
+ if (isAgentAvatarValid())
+ { // joint numbers are consistent inside LLVOAvatar and animations, but inconsistent inside meshes,
+ // generate a map of mesh joint numbers to LLVOAvatar joint numbers
+ LLSkinningUtil::initJointNums(info, gAgentAvatarp);
}
- // LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL;
+ // remember the skin info in the background thread so we can use it
+ // to calculate per-joint bounding boxes when volumes are loaded
+ mSkinMap[mesh_id] = info;
+
{
LLMutexLock lock(mMutex);
mSkinInfoQ.push_back(info);
@@ -2265,10 +2301,10 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
S32 mesh_num = 0;
S32 texture_num = 0;
- std::set<LLViewerTexture* > textures;
- std::map<LLViewerTexture*,S32> texture_index;
+ std::unordered_set<LLViewerTexture* > textures;
+ std::unordered_map<LLViewerTexture*,S32> texture_index;
- std::map<LLModel*,S32> mesh_index;
+ std::unordered_map<LLModel*,S32> mesh_index;
std::string model_name;
S32 instance_num = 0;
@@ -2957,7 +2993,7 @@ void LLMeshRepoThread::notifyLoadedMeshes()
{
if (mMutex->trylock())
{
- std::deque<LLMeshSkinInfo*> skin_info_q;
+ std::deque<LLPointer<LLMeshSkinInfo>> skin_info_q;
std::deque<UUIDBasedRequest> skin_info_unavail_q;
std::list<LLModel::Decomposition*> decomp_q;
@@ -3080,6 +3116,7 @@ S32 LLMeshRepository::getActualMeshLOD(LLMeshHeader& header, S32 lod)
// are cases far off the norm.
void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
{
+ LL_PROFILE_ZONE_SCOPED;
mProcessed = true;
unsigned int retries(0U);
@@ -3356,6 +3393,7 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)
void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
U8 * data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
if ((!MESH_LOD_PROCESS_FAILED)
&& ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong
{
@@ -3421,6 +3459,7 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
U8 * data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
if ((!MESH_SKIN_INFO_PROCESS_FAILED)
&& ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong
&& gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
@@ -3470,6 +3509,7 @@ void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status)
void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
U8 * data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
if ((!MESH_DECOMP_PROCESS_FAILED)
&& ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong
&& gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))
@@ -3517,6 +3557,7 @@ void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status)
void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
U8 * data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
if ((!MESH_PHYS_SHAPE_PROCESS_FAILED)
&& ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong
&& gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size) == MESH_OK)
@@ -3870,6 +3911,13 @@ void LLMeshRepository::notifyLoadedMeshes()
{
mSkinMap.erase(copy_iter);
}
+
+ // erase from background thread
+ LLUUID id = iter->first;
+ mThread->mWorkQueue.post([=]()
+ {
+ mThread->mSkinMap.erase(id);
+ });
}
//LL_INFOS() << "Skin info cache elements:" << mSkinMap.size() << " Memory: " << U64Kilobytes(skinbytes) << LL_ENDL;
}
@@ -4206,7 +4254,7 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
{
LLMutexLock lock(mMeshMutex);
//add volume to list of loading meshes
- std::set<LLUUID>::iterator iter = mLoadingPhysicsShapes.find(mesh_id);
+ std::unordered_set<LLUUID>::iterator iter = mLoadingPhysicsShapes.find(mesh_id);
if (iter == mLoadingPhysicsShapes.end())
{ //no request pending for this skin info
// *FIXME: Nothing ever deletes entries, can't be right
@@ -4236,7 +4284,7 @@ LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id
{
LLMutexLock lock(mMeshMutex);
//add volume to list of loading meshes
- std::set<LLUUID>::iterator iter = mLoadingDecompositions.find(mesh_id);
+ std::unordered_set<LLUUID>::iterator iter = mLoadingDecompositions.find(mesh_id);
if (iter == mLoadingDecompositions.end())
{ //no request pending for this skin info
mLoadingDecompositions.insert(mesh_id);
@@ -4287,6 +4335,8 @@ bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id)
bool LLMeshRepository::hasSkinInfo(const LLUUID& mesh_id)
{
+ LL_PROFILE_ZONE_SCOPED;
+
if (mesh_id.isNull())
{
return false;
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index b850ade0bb..b5c2424578 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -28,6 +28,7 @@
#define LL_MESH_REPOSITORY_H
#include <unordered_map>
+#include <unordered_set>
#include "llassettype.h"
#include "llmodel.h"
#include "lluuid.h"
@@ -341,7 +342,7 @@ public:
std::deque<UUIDBasedRequest> mSkinRequests;
// list of completed skin info requests
- std::deque<LLMeshSkinInfo*> mSkinInfoQ;
+ std::deque<LLPointer<LLMeshSkinInfo>> mSkinInfoQ;
// list of skin info requests that have failed or are unavailaibe
std::deque<UUIDBasedRequest> mSkinUnavailableQ;
@@ -368,9 +369,17 @@ public:
std::deque<LoadedMesh> mLoadedQ;
//map of pending header requests and currently desired LODs
- typedef boost::unordered_map<LLUUID, std::vector<S32> > pending_lod_map;
+ typedef std::unordered_map<LLUUID, std::vector<S32> > pending_lod_map;
pending_lod_map mPendingLOD;
+ // map of mesh ID to skin info (mirrors LLMeshRepository::mSkinMap)
+ /// NOTE: LLMeshRepository::mSkinMap is accessed very frequently, so maintain a copy here to avoid mutex overhead
+ typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map;
+ skin_map mSkinMap;
+
+ // workqueue for processing generic requests
+ LL::WorkQueue mWorkQueue;
+
// llcorehttp library interface objects.
LLCore::HttpStatus mHttpStatus;
LLCore::HttpRequest * mHttpRequest;
@@ -380,7 +389,7 @@ public:
LLCore::HttpRequest::policy_t mHttpPolicyClass;
LLCore::HttpRequest::policy_t mHttpLargePolicyClass;
- typedef std::set<LLCore::HttpHandler::ptr_t> http_request_set;
+ typedef std::unordered_set<LLCore::HttpHandler::ptr_t> http_request_set;
http_request_set mHttpRequestSet; // Outstanding HTTP requests
std::string mGetMeshCapability;
@@ -696,13 +705,13 @@ public:
std::queue<LLUUID> mPendingSkinRequests;
//list of mesh ids awaiting decompositions
- std::set<LLUUID> mLoadingDecompositions;
+ std::unordered_set<LLUUID> mLoadingDecompositions;
//list of mesh ids that need to send decomposition fetch requests
std::queue<LLUUID> mPendingDecompositionRequests;
//list of mesh ids awaiting physics shapes
- std::set<LLUUID> mLoadingPhysicsShapes;
+ std::unordered_set<LLUUID> mLoadingPhysicsShapes;
//list of mesh ids that need to send physics shape fetch requests
std::queue<LLUUID> mPendingPhysicsShapeRequests;
diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp
index 1c92e06700..cee43f3cff 100644
--- a/indra/newview/llskinningutil.cpp
+++ b/indra/newview/llskinningutil.cpp
@@ -317,19 +317,16 @@ void LLSkinningUtil::initJointNums(LLMeshSkinInfo* skin, LLVOAvatar *avatar)
void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *avatar, LLVolumeFace& vol_face)
{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
-
if (vol_face.mJointRiggingInfoTab.needsUpdate())
{
S32 num_verts = vol_face.mNumVertices;
S32 num_joints = static_cast<S32>(skin->mJointNames.size());
if (num_verts > 0 && vol_face.mWeights && num_joints > 0)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar);
if (vol_face.mJointRiggingInfoTab.size()==0)
{
- //std::set<S32> active_joints;
- //S32 active_verts = 0;
vol_face.mJointRiggingInfoTab.resize(LL_CHARACTER_MAX_ANIMATED_JOINTS);
LLJointRiggingInfoTab &rig_info_tab = vol_face.mJointRiggingInfoTab;
for (S32 i=0; i<vol_face.mNumVertices; i++)
@@ -345,35 +342,22 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a
F32 w = weights[k];
idx[k] = llclamp((S32) floorf(w), (S32)0, (S32)LL_CHARACTER_MAX_ANIMATED_JOINTS-1);
wght[k] = w - idx[k];
- scale += wght[k];
- }
- if (scale > 0.0f)
- {
- for (U32 k=0; k<4; ++k)
- {
- wght[k] /= scale;
- }
}
+
for (U32 k=0; k<4; ++k)
{
S32 joint_index = idx[k];
- if (wght[k] > 0.0f && num_joints > joint_index)
+ if (wght[k] > 0.2f && num_joints > joint_index)
{
S32 joint_num = skin->mJointNums[joint_index];
if (joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS)
{
rig_info_tab[joint_num].setIsRiggedTo(true);
- // FIXME could precompute these matMuls.
- const LLMatrix4a& bind_shape = skin->mBindShapeMatrix;
- const LLMatrix4a& inv_bind = skin->mInvBindMatrix[joint_index];
- LLMatrix4a mat;
+ const LLMatrix4a& mat = skin->mBindPoseMatrix[joint_index];
LLVector4a pos_joint_space;
- matMul(bind_shape, inv_bind, mat);
-
mat.affineTransform(pos, pos_joint_space);
- pos_joint_space.mul(wght[k]);
LLVector4a *extents = rig_info_tab[joint_num].getRiggedExtents();
update_min_max(extents[0], extents[1], pos_joint_space);
@@ -381,28 +365,9 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a
}
}
}
- //LL_DEBUGS("RigSpammish") << "built rigging info for vf " << &vol_face
- // << " num_verts " << vol_face.mNumVertices
- // << " active joints " << active_joints.size()
- // << " active verts " << active_verts
- // << LL_ENDL;
vol_face.mJointRiggingInfoTab.setNeedsUpdate(false);
}
}
-
-#if DEBUG_SKINNING
- if (vol_face.mJointRiggingInfoTab.size()!=0)
- {
- LL_DEBUGS("RigSpammish") << "we have rigging info for vf " << &vol_face
- << " num_verts " << vol_face.mNumVertices << LL_ENDL;
- }
- else
- {
- LL_DEBUGS("RigSpammish") << "no rigging info for vf " << &vol_face
- << " num_verts " << vol_face.mNumVertices << LL_ENDL;
- }
-#endif
-
}
}
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index e36a03a749..86440fca48 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -7280,6 +7280,7 @@ const std::string& LLViewerObject::getAttachmentItemName() const
//virtual
LLVOAvatar* LLViewerObject::getAvatar() const
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
if (getControlAvatar())
{
return getControlAvatar();
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 8e8cf7424f..ef0bb3b926 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -1646,6 +1646,9 @@ void LLVOAvatar::renderCollisionVolumes()
}
}
+// defined in llspatialpartition.cpp -- draw a box outline in the current GL context from given center and half-size
+void drawBoxOutline(const LLVector4a& pos, const LLVector4a& size);
+
void LLVOAvatar::renderBones(const std::string &selected_joint)
{
LLGLEnable blend(GL_BLEND);
@@ -1722,6 +1725,88 @@ void LLVOAvatar::renderBones(const std::string &selected_joint)
gGL.popMatrix();
}
+
+
+ // draw joint space bounding boxes of rigged attachments in yellow
+ gGL.color3f(1.f, 1.f, 0.f);
+ for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++)
+ {
+ LLJoint* joint = getJoint(joint_num);
+ LLJointRiggingInfo* rig_info = NULL;
+ if (joint_num < mJointRiggingInfoTab.size())
+ {
+ rig_info = &mJointRiggingInfoTab[joint_num];
+ }
+
+ if (joint && rig_info && rig_info->isRiggedTo())
+ {
+ LLViewerJointAttachment* as_joint_attach = dynamic_cast<LLViewerJointAttachment*>(joint);
+ if (as_joint_attach && as_joint_attach->getIsHUDAttachment())
+ {
+ // Ignore bounding box of HUD joints
+ continue;
+ }
+ gGL.pushMatrix();
+ gGL.multMatrix(&joint->getXform()->getWorldMatrix().mMatrix[0][0]);
+
+ LLVector4a pos;
+ LLVector4a size;
+
+ const LLVector4a* extents = rig_info->getRiggedExtents();
+
+ pos.setAdd(extents[0], extents[1]);
+ pos.mul(0.5f);
+ size.setSub(extents[1], extents[0]);
+ size.mul(0.5f);
+
+ drawBoxOutline(pos, size);
+
+ gGL.popMatrix();
+ }
+ }
+
+ // draw world space attachment rigged bounding boxes in cyan
+ gGL.color3f(0.f, 1.f, 1.f);
+ for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
+ iter != mAttachmentPoints.end();
+ ++iter)
+ {
+ LLViewerJointAttachment* attachment = iter->second;
+
+ if (attachment->getValid())
+ {
+ for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
+ attachment_iter != attachment->mAttachedObjects.end();
+ ++attachment_iter)
+ {
+ LLViewerObject* attached_object = attachment_iter->get();
+ if (attached_object && !attached_object->isHUDAttachment())
+ {
+ LLDrawable* drawable = attached_object->mDrawable;
+ if (drawable && drawable->isState(LLDrawable::RIGGED | LLDrawable::RIGGED_CHILD))
+ {
+ // get face rigged extents
+ for (S32 i = 0; i < drawable->getNumFaces(); ++i)
+ {
+ LLFace* facep = drawable->getFace(i);
+ if (facep && facep->isState(LLFace::RIGGED))
+ {
+ LLVector4a center, size;
+
+ LLVector4a* extents = facep->mRiggedExtents;
+
+ center.setAdd(extents[0], extents[1]);
+ center.mul(0.5f);
+ size.setSub(extents[1], extents[0]);
+ size.mul(0.5f);
+ drawBoxOutline(center, size);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
@@ -10662,35 +10747,42 @@ void LLVOAvatar::updateRiggingInfo()
LL_DEBUGS("RigSpammish") << getFullname() << " updating rig tab" << LL_ENDL;
- std::vector<LLVOVolume*> volumes;
+ // use a local static for scratch space to avoid reallocation here
+ static std::vector<LLVOVolume*> volumes;
+ volumes.resize(0);
getAssociatedVolumes(volumes);
- std::map<LLUUID, S32> curr_rigging_info_key;
-
{
LL_PROFILE_ZONE_NAMED_CATEGORY_AVATAR("update rig info - get key")
+ HBXXH128 hash;
// Get current rigging info key
for (LLVOVolume* vol : volumes)
{
- if (vol->isMesh() && vol->getVolume())
+ if (vol->isRiggedMesh())
{
const LLUUID& mesh_id = vol->getVolume()->getParams().getSculptID();
S32 max_lod = llmax(vol->getLOD(), vol->mLastRiggingInfoLOD);
- curr_rigging_info_key[mesh_id] = max_lod;
+
+ hash.update(mesh_id.mData, sizeof(mesh_id.mData));
+ hash.update(&max_lod, sizeof(max_lod));
}
}
- }
- // Check for key change, which indicates some change in volume composition or LOD.
- if (curr_rigging_info_key == mLastRiggingInfoKey)
- {
- return;
+ LLUUID curr_rigging_info_key = hash.digest();
+
+ // Check for key change, which indicates some change in volume composition or LOD.
+ if (curr_rigging_info_key == mLastRiggingInfoKey)
+ {
+ return;
+ }
+
+
+ // Something changed. Update.
+ mLastRiggingInfoKey = curr_rigging_info_key;
}
- // Something changed. Update.
- mLastRiggingInfoKey = curr_rigging_info_key;
mJointRiggingInfoTab.clear();
for (LLVOVolume* vol : volumes)
{
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index a93ccf46bf..ff1cbc34fc 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -226,7 +226,7 @@ public:
// virtual
void updateRiggingInfo();
// This encodes mesh id and LOD, so we can see whether display is up-to-date.
- std::map<LLUUID,S32> mLastRiggingInfoKey;
+ LLUUID mLastRiggingInfoKey;
std::set<LLUUID> mActiveOverrideMeshes;
virtual void onActiveOverrideMeshesChanged();
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 4f48a070e3..2859f8b1c2 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -3657,7 +3657,7 @@ const LLMeshSkinInfo* LLVOVolume::getSkinInfo() const
// virtual
bool LLVOVolume::isRiggedMesh() const
{
- return isMesh() && getSkinInfo();
+ return getSkinInfo() != nullptr;
}
//----------------------------------------------------------------------------
@@ -3818,7 +3818,6 @@ void LLVOVolume::updateRiggingInfo()
LLVolume *volume = getVolume();
if (skin && avatar && volume)
{
- LL_DEBUGS("RigSpammish") << "starting, vovol " << this << " lod " << getLOD() << " last " << mLastRiggingInfoLOD << LL_ENDL;
if (getLOD()>mLastRiggingInfoLOD || getLOD()==3)
{
// Rigging info may need update
@@ -3834,9 +3833,6 @@ void LLVOVolume::updateRiggingInfo()
}
// Keep the highest LOD info available.
mLastRiggingInfoLOD = getLOD();
- LL_DEBUGS("RigSpammish") << "updated rigging info for LLVOVolume "
- << this << " lod " << mLastRiggingInfoLOD
- << LL_ENDL;
}
}
}