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
master
Dave Parks 2024-09-16 18:49:03 -05:00 committed by GitHub
parent 42975dfd88
commit 486613e79b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 347 additions and 105 deletions

View File

@ -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);

View File

@ -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();
}

View File

@ -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;

View File

@ -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];
}
}
}
gGL.begin(LLRender::TRIANGLES);
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++)
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++)
{
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;
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);
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();
}
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++)
{
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.end();
}
gGL.end();
}

View File

@ -45,6 +45,7 @@ public:
private:
std::vector< std::vector<LLVector3> > mSpherePoints;
LLPointer<LLVertexBuffer> mVertexBuffer;
};
extern LLRenderSphere gSphere;

View File

@ -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;

View File

@ -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();

View File

@ -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].

View File

@ -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;

View File

@ -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;

View File

@ -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
}
}

View File

@ -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();

View File

@ -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));
}
}
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;
}
// 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;
mJointRiggingInfoTab.clear();
for (LLVOVolume* vol : volumes)
{

View File

@ -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();

View File

@ -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;
}
}
}