summaryrefslogtreecommitdiff
path: root/indra/newview/llmeshrepository.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llmeshrepository.cpp')
-rw-r--r--indra/newview/llmeshrepository.cpp2009
1 files changed, 1293 insertions, 716 deletions
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 93453f507c..c0b1a5326a 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -255,12 +255,13 @@
// mSkinInfoQ mMutex rw.repo.mMutex, rw.main.mMutex [5] (was: [0])
// mDecompositionRequests mMutex rw.repo.mMutex, ro.repo.none [5]
// mPhysicsShapeRequests mMutex rw.repo.mMutex, ro.repo.none [5]
-// mDecompositionQ mMutex rw.repo.mMutex, rw.main.mMutex [5] (was: [0])
+// mDecompositionQ mMutex rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was: [0])
+// mPhysicsQ mMutex rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was: [0])
// mHeaderReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex
// mLODReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex
-// mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mMutex
-// mLoadedQ mMutex rw.repo.mMutex, ro.main.none [5], rw.main.mMutex
-// mPendingLOD mMutex rw.repo.mMutex, rw.any.mMutex
+// mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mLoadedMutex
+// mLoadedQ mMutex rw.repo.mLoadedMutex, ro.main.none [5], rw.main.mLoadedMutex
+// mPendingLOD mMutex rw.repo.mPendingMutex, rw.any.mPendingMutex
// mGetMeshCapability mMutex rw.main.mMutex, ro.repo.mMutex (was: [0])
// mGetMesh2Capability mMutex rw.main.mMutex, ro.repo.mMutex (was: [0])
// mGetMeshVersion mMutex rw.main.mMutex, ro.repo.mMutex
@@ -343,20 +344,22 @@ static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");
LLMeshRepository gMeshRepo;
-const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space
+constexpr U32 CACHE_PREAMBLE_VERSION = 1;
+constexpr S32 CACHE_PREAMBLE_SIZE = sizeof(U32) * 3; //version, header_size, flags
+constexpr S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space
-const S32 REQUEST2_HIGH_WATER_MIN = 32; // Limits for GetMesh2 regions
-const S32 REQUEST2_HIGH_WATER_MAX = 100;
-const S32 REQUEST2_LOW_WATER_MIN = 16;
-const S32 REQUEST2_LOW_WATER_MAX = 50;
+constexpr S32 REQUEST2_HIGH_WATER_MIN = 32; // Limits for GetMesh2 regions
+constexpr S32 REQUEST2_HIGH_WATER_MAX = 100;
+constexpr S32 REQUEST2_LOW_WATER_MIN = 16;
+constexpr S32 REQUEST2_LOW_WATER_MAX = 50;
-const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue
-const long SMALL_MESH_XFER_TIMEOUT = 120L; // Seconds to complete xfer, small mesh downloads
-const long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads
+constexpr U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue
+constexpr long SMALL_MESH_XFER_TIMEOUT = 120L; // Seconds to complete xfer, small mesh downloads
+constexpr long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads
-const U32 DOWNLOAD_RETRY_LIMIT = 8;
-const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds
+constexpr U32 DOWNLOAD_RETRY_LIMIT = 8;
+constexpr F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds
// Would normally like to retry on uploads as some
// retryable failures would be recoverable. Unfortunately,
@@ -366,7 +369,7 @@ const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds
// cap which then produces a 404 on retry destroying some
// (occasionally) useful error information. We'll leave
// upload retries to the user as in the past. SH-4667.
-const long UPLOAD_RETRY_LIMIT = 0L;
+constexpr long UPLOAD_RETRY_LIMIT = 0L;
// Maximum mesh version to support. Three least significant digits are reserved for the minor version,
// with major version changes indicating a format change that is not backwards compatible and should not
@@ -374,7 +377,7 @@ const long UPLOAD_RETRY_LIMIT = 0L;
// present, the version is 0.001. A viewer that can parse version 0.001 can also parse versions up to 0.999,
// but not 1.0 (integer 1000).
// See wiki at https://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format
-const S32 MAX_MESH_VERSION = 999;
+constexpr S32 MAX_MESH_VERSION = 999;
U32 LLMeshRepository::sBytesReceived = 0;
U32 LLMeshRepository::sMeshRequestCount = 0;
@@ -386,12 +389,12 @@ U32 LLMeshRepository::sLODProcessing = 0;
U32 LLMeshRepository::sLODPending = 0;
U32 LLMeshRepository::sCacheBytesRead = 0;
-U32 LLMeshRepository::sCacheBytesWritten = 0;
+std::atomic<U32> LLMeshRepository::sCacheBytesWritten = 0;
U32 LLMeshRepository::sCacheBytesHeaders = 0;
U32 LLMeshRepository::sCacheBytesSkins = 0;
U32 LLMeshRepository::sCacheBytesDecomps = 0;
U32 LLMeshRepository::sCacheReads = 0;
-U32 LLMeshRepository::sCacheWrites = 0;
+std::atomic<U32> LLMeshRepository::sCacheWrites = 0;
U32 LLMeshRepository::sMaxLockHoldoffs = 0;
LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false); // true -> gather cpu metrics
@@ -542,6 +545,66 @@ bool RequestStats::isDelayed() const
return mTimer.getStarted() && !mTimer.hasExpired();
}
+F32 calculate_score(LLVOVolume* object)
+{
+ if (!object)
+ {
+ return -1.f;
+ }
+ LLDrawable* drawable = object->mDrawable;
+ if (!drawable)
+ {
+ return -1;
+ }
+ if (drawable->isState(LLDrawable::RIGGED) || object->isAttachment())
+ {
+ LLVOAvatar* avatar = object->getAvatar();
+ LLDrawable* av_drawable = avatar ? avatar->mDrawable : nullptr;
+ if (avatar && av_drawable)
+ {
+ // See LLVOVolume::calcLOD()
+ F32 radius;
+ if (avatar->isControlAvatar())
+ {
+ const LLVector3* box = avatar->getLastAnimExtents();
+ LLVector3 diag = box[1] - box[0];
+ radius = diag.magVec() * 0.5f;
+ }
+ else
+ {
+ // Volume in a rigged mesh attached to a regular avatar.
+ const LLVector3* box = avatar->getLastAnimExtents();
+ LLVector3 diag = box[1] - box[0];
+ radius = diag.magVec();
+
+ if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData())
+ {
+ // slightly deprioritize avatars that are still receiving data
+ radius *= 0.9f;
+ }
+ }
+ return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f);
+ }
+ }
+ return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f);
+}
+
+void PendingRequestBase::updateScore()
+{
+ mScore = 0;
+ if (mTrackedData)
+ {
+ for (LLVOVolume* volume : mTrackedData->mVolumes)
+ {
+ F32 cur_score = calculate_score(volume);
+ if (cur_score > 0)
+ {
+ mScore = llmax(mScore, cur_score);
+ }
+ }
+ }
+}
+
LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMaterial& material)
{
LLPointer< LLViewerFetchedTexture > * ppTex = static_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData);
@@ -550,6 +613,7 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate
std::atomic<S32> LLMeshRepoThread::sActiveHeaderRequests = 0;
std::atomic<S32> LLMeshRepoThread::sActiveLODRequests = 0;
+std::atomic<S32> LLMeshRepoThread::sActiveSkinRequests = 0;
U32 LLMeshRepoThread::sMaxConcurrentRequests = 1;
S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN;
S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN;
@@ -584,17 +648,16 @@ public:
: LLCore::HttpHandler(),
mMeshParams(),
mProcessed(false),
+ mHasDataOwnership(true),
mHttpHandle(LLCORE_HTTP_HANDLE_INVALID),
mOffset(offset),
mRequestedBytes(requested_bytes)
{}
- virtual ~LLMeshHandlerBase()
- {}
+ virtual ~LLMeshHandlerBase() = default;
-protected:
- LLMeshHandlerBase(const LLMeshHandlerBase &); // Not defined
- void operator=(const LLMeshHandlerBase &); // Not defined
+ LLMeshHandlerBase(const LLMeshHandlerBase &) = delete;
+ LLMeshHandlerBase& operator=(const LLMeshHandlerBase&) = delete;
public:
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
@@ -607,6 +670,9 @@ public:
LLCore::HttpHandle mHttpHandle;
U32 mOffset;
U32 mRequestedBytes;
+
+protected:
+ bool mHasDataOwnership = true;
};
@@ -625,9 +691,8 @@ public:
}
virtual ~LLMeshHeaderHandler();
-protected:
- LLMeshHeaderHandler(const LLMeshHeaderHandler &); // Not defined
- void operator=(const LLMeshHeaderHandler &); // Not defined
+ LLMeshHeaderHandler(const LLMeshHeaderHandler&) = delete;
+ LLMeshHeaderHandler& operator=(const LLMeshHeaderHandler&) = delete;
public:
virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);
@@ -651,14 +716,16 @@ public:
}
virtual ~LLMeshLODHandler();
-protected:
- LLMeshLODHandler(const LLMeshLODHandler &); // Not defined
- void operator=(const LLMeshLODHandler &); // Not defined
+ LLMeshLODHandler(const LLMeshLODHandler&) = delete;
+ LLMeshLODHandler& operator=(const LLMeshLODHandler&) = delete;
public:
virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);
virtual void processFailure(LLCore::HttpStatus status);
+private:
+ void processLod(U8* data, S32 data_size);
+
public:
S32 mLOD;
};
@@ -674,12 +741,15 @@ public:
LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 requested_bytes)
: LLMeshHandlerBase(offset, requested_bytes),
mMeshID(id)
- {}
+ {
+ LLMeshRepoThread::incActiveSkinRequests();
+ }
virtual ~LLMeshSkinInfoHandler();
-protected:
- LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &); // Not defined
- void operator=(const LLMeshSkinInfoHandler &); // Not defined
+ LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler&) = delete;
+ LLMeshSkinInfoHandler& operator=(const LLMeshSkinInfoHandler&) = delete;
+
+ void processSkin(U8* data, S32 data_size);
public:
virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);
@@ -703,9 +773,8 @@ public:
{}
virtual ~LLMeshDecompositionHandler();
-protected:
- LLMeshDecompositionHandler(const LLMeshDecompositionHandler &); // Not defined
- void operator=(const LLMeshDecompositionHandler &); // Not defined
+ LLMeshDecompositionHandler(const LLMeshDecompositionHandler&) = delete;
+ LLMeshDecompositionHandler& operator=(const LLMeshDecompositionHandler&) = delete;
public:
virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);
@@ -729,9 +798,8 @@ public:
{}
virtual ~LLMeshPhysicsShapeHandler();
-protected:
- LLMeshPhysicsShapeHandler(const LLMeshPhysicsShapeHandler &); // Not defined
- void operator=(const LLMeshPhysicsShapeHandler &); // Not defined
+ LLMeshPhysicsShapeHandler(const LLMeshPhysicsShapeHandler&) = delete;
+ LLMeshPhysicsShapeHandler& operator=(const LLMeshPhysicsShapeHandler&) = delete;
public:
virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);
@@ -742,8 +810,12 @@ public:
};
-void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
- const char * const stage, const std::string & model_name)
+void log_upload_error(
+ LLCore::HttpStatus status,
+ const LLSD& content,
+ const char * const stage,
+ const std::string & model_name,
+ const std::vector<std::string> & texture_filenames)
{
// Add notification popup.
LLSD args;
@@ -801,6 +873,20 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
error_num++;
}
}
+
+ if (err.has("TextureIndex"))
+ {
+ S32 texture_index = err["TextureIndex"].asInteger();
+ if (texture_index < texture_filenames.size())
+ {
+ args["MESSAGE"] = message + "\n" + texture_filenames[texture_index];
+ }
+ else
+ {
+ llassert(false); // figure out why or how texture wasn't in the list
+ args["MESSAGE"] = message + llformat("\nTexture index: %d", texture_index);
+ }
+ }
}
else
{
@@ -823,6 +909,14 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
gMeshRepo.uploadError(args);
}
+void write_preamble(LLFileSystem &file, S32 header_bytes, S32 flags)
+{
+ LLMeshRepository::sCacheBytesWritten += CACHE_PREAMBLE_SIZE;
+ file.write((U8*)&CACHE_PREAMBLE_VERSION, sizeof(U32));
+ file.write((U8*)&header_bytes, sizeof(U32));
+ file.write((U8*)&flags, sizeof(U32));
+}
+
LLMeshRepoThread::LLMeshRepoThread()
: LLThread("mesh repo"),
mHttpRequest(NULL),
@@ -837,18 +931,26 @@ LLMeshRepoThread::LLMeshRepoThread()
mMutex = new LLMutex();
mHeaderMutex = new LLMutex();
+ mLoadedMutex = new LLMutex();
+ mPendingMutex = new LLMutex();
+ mSkinMapMutex = new LLMutex();
mSignal = new LLCondition();
mHttpRequest = new LLCore::HttpRequest;
- mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
+ mHttpOptions = std::make_shared<LLCore::HttpOptions>();
mHttpOptions->setTransferTimeout(SMALL_MESH_XFER_TIMEOUT);
mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter"));
- mHttpLargeOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
+ mHttpLargeOptions = std::make_shared<LLCore::HttpOptions>();
mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT);
mHttpLargeOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter"));
- mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders);
+ mHttpHeaders = std::make_shared<LLCore::HttpHeaders>();
mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_VND_LL_MESH);
mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH2);
mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH);
+
+ // Lod processing is expensive due to the number of requests
+ // and a need to do expensive cacheOptimize().
+ mMeshThreadPool = std::make_unique<LL::ThreadPool>("MeshLodProcessing", 2);
+ mMeshThreadPool->start();
}
@@ -874,14 +976,28 @@ LLMeshRepoThread::~LLMeshRepoThread()
mDecompositionQ.pop_front();
}
+ while (!mPhysicsQ.empty())
+ {
+ delete mPhysicsQ.front();
+ mPhysicsQ.pop_front();
+ }
+
delete mHttpRequest;
- mHttpRequest = NULL;
+ mHttpRequest = nullptr;
delete mMutex;
- mMutex = NULL;
+ mMutex = nullptr;
delete mHeaderMutex;
- mHeaderMutex = NULL;
+ mHeaderMutex = nullptr;
+ delete mLoadedMutex;
+ mLoadedMutex = nullptr;
+ delete mPendingMutex;
+ mPendingMutex = nullptr;
+ delete mSkinMapMutex;
+ mSkinMapMutex = nullptr;
delete mSignal;
- mSignal = NULL;
+ mSignal = nullptr;
+ delete[] mDiskCacheBuffer;
+ mDiskCacheBuffer = nullptr;
}
void LLMeshRepoThread::run()
@@ -908,6 +1024,7 @@ void LLMeshRepoThread::run()
// On the other hand, this may actually be an effective and efficient scheme...
mSignal->wait();
+ LL_PROFILE_ZONE_NAMED("mesh_thread_loop")
if (LLApp::isExiting())
{
@@ -925,11 +1042,55 @@ void LLMeshRepoThread::run()
}
sRequestWaterLevel = static_cast<S32>(mHttpRequestSet.size()); // Stats data update
- // NOTE: order of queue processing intentionally favors LOD requests over header requests
+ // NOTE: order of queue processing intentionally favors LOD and Skin requests over header requests
// Todo: we are processing mLODReqQ, mHeaderReqQ, mSkinRequests, mDecompositionRequests and mPhysicsShapeRequests
// in relatively similar manners, remake code to simplify/unify the process,
// like processRequests(&requestQ, fetchFunction); which does same thing for each element
+ if (mHttpRequestSet.size() < sRequestHighWater
+ && !mSkinRequests.empty())
+ {
+ if (!mSkinRequests.empty())
+ {
+ std::list<UUIDBasedRequest> incomplete;
+ while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
+ {
+
+ mMutex->lock();
+ auto req = mSkinRequests.front();
+ mSkinRequests.pop_front();
+ mMutex->unlock();
+ if (req.isDelayed())
+ {
+ incomplete.emplace_back(req);
+ }
+ else if (!fetchMeshSkinInfo(req.mId))
+ {
+ if (req.canRetry())
+ {
+ req.updateTime();
+ incomplete.emplace_back(req);
+ }
+ else
+ {
+ LLMutexLock locker(mLoadedMutex);
+ mSkinUnavailableQ.push_back(req);
+ LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL;
+ }
+ }
+ }
+
+ if (!incomplete.empty())
+ {
+ LLMutexLock locker(mMutex);
+ for (const auto& req : incomplete)
+ {
+ mSkinRequests.push_back(req);
+ }
+ }
+ }
+ }
+
if (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
{
std::list<LODRequest> incomplete;
@@ -950,7 +1111,7 @@ void LLMeshRepoThread::run()
// failed to load before, wait a bit
incomplete.push_front(req);
}
- else if (!fetchMeshLOD(req.mMeshParams, req.mLOD, req.canRetry()))
+ else if (!fetchMeshLOD(req.mMeshParams, req.mLOD))
{
if (req.canRetry())
{
@@ -961,7 +1122,7 @@ void LLMeshRepoThread::run()
else
{
// too many fails
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mUnavailableQ.push_back(req);
LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL;
}
@@ -998,7 +1159,7 @@ void LLMeshRepoThread::run()
// failed to load before, wait a bit
incomplete.push_front(req);
}
- else if (!fetchMeshHeader(req.mMeshParams, req.canRetry()))
+ else if (!fetchMeshHeader(req.mMeshParams))
{
if (req.canRetry())
{
@@ -1028,54 +1189,13 @@ void LLMeshRepoThread::run()
// performing long-duration actions.
if (mHttpRequestSet.size() < sRequestHighWater
- && (!mSkinRequests.empty()
- || !mDecompositionRequests.empty()
+ && (!mDecompositionRequests.empty()
|| !mPhysicsShapeRequests.empty()))
{
// Something to do probably, lock and double-check. We don't want
// to hold the lock long here. That will stall main thread activities
// so we bounce it.
- if (!mSkinRequests.empty())
- {
- std::list<UUIDBasedRequest> incomplete;
- while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
- {
-
- mMutex->lock();
- auto req = mSkinRequests.front();
- mSkinRequests.pop_front();
- mMutex->unlock();
- if (req.isDelayed())
- {
- incomplete.emplace_back(req);
- }
- else if (!fetchMeshSkinInfo(req.mId, req.canRetry()))
- {
- if (req.canRetry())
- {
- req.updateTime();
- incomplete.emplace_back(req);
- }
- else
- {
- LLMutexLock locker(mMutex);
- mSkinUnavailableQ.push_back(req);
- LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL;
- }
- }
- }
-
- if (!incomplete.empty())
- {
- LLMutexLock locker(mMutex);
- for (const auto& req : incomplete)
- {
- mSkinRequests.push_back(req);
- }
- }
- }
-
// holding lock, try next list
// *TODO: For UI/debug-oriented lists, we might drop the fine-
// grained locking as there's a lowered expectation of smoothness
@@ -1103,7 +1223,7 @@ void LLMeshRepoThread::run()
}
else
{
- LL_DEBUGS() << "mDecompositionRequests failed: " << req.mId << LL_ENDL;
+ LL_DEBUGS(LOG_MESH) << "mDecompositionRequests failed: " << req.mId << LL_ENDL;
}
}
}
@@ -1139,7 +1259,7 @@ void LLMeshRepoThread::run()
}
else
{
- LL_DEBUGS() << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL;
+ LL_DEBUGS(LOG_MESH) << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL;
}
}
}
@@ -1168,6 +1288,12 @@ void LLMeshRepoThread::run()
LL_WARNS(LOG_MESH) << "Convex decomposition unable to be quit." << LL_ENDL;
}
}
+void LLMeshRepoThread::cleanup()
+{
+ mShuttingDown = true;
+ mSignal->broadcast();
+ mMeshThreadPool->close();
+}
// Mutex: LLMeshRepoThread::mMutex must be held on entry
void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
@@ -1195,40 +1321,91 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32
}
}
-
void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
{ //could be called from any thread
const LLUUID& mesh_id = mesh_params.getSculptID();
- LLMutexLock lock(mMutex);
- LLMutexLock header_lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
- if (iter != mMeshHeader.end())
+ loadMeshLOD(mesh_id, mesh_params, lod);
+}
+
+void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& mesh_params, S32 lod)
+{
+ if (hasHeader(mesh_id))
{ //if we have the header, request LOD byte range
LODRequest req(mesh_params, lod);
{
+ LLMutexLock lock(mMutex);
mLODReqQ.push(req);
LLMeshRepository::sLODProcessing++;
}
}
else
{
+ LLMutexLock lock(mPendingMutex);
HeaderRequest req(mesh_params);
pending_lod_map::iterator pending = mPendingLOD.find(mesh_id);
if (pending != mPendingLOD.end())
- { //append this lod request to existing header request
- pending->second.push_back(lod);
- llassert(pending->second.size() <= LLModel::NUM_LODS);
+ {
+ //append this lod request to existing header request
+ if (lod < LLModel::NUM_LODS && lod >= 0)
+ {
+ pending->second[lod]++;
+ }
+ else
+ {
+ LL_WARNS(LOG_MESH) << "Invalid LOD request: " << lod << "for mesh" << mesh_id << LL_ENDL;
+ }
+ llassert_msg(lod < LLModel::NUM_LODS, "Requested lod is out of bounds");
}
else
- { //if no header request is pending, fetch header
+ {
+ //if no header request is pending, fetch header
+ auto& array = mPendingLOD[mesh_id];
+ std::fill(array.begin(), array.end(), 0);
+ array[lod]++;
+
+ LLMutexLock lock(mMutex);
mHeaderReqQ.push(req);
- mPendingLOD[mesh_id].push_back(lod);
}
}
}
+U8* LLMeshRepoThread::getDiskCacheBuffer(S32 size)
+{
+ if (mDiskCacheBufferSize < size)
+ {
+ const S32 MINIMUM_BUFFER_SIZE = 8192; // a minimum to avoid frequent early reallocations
+ size = llmax(MINIMUM_BUFFER_SIZE, size);
+ delete[] mDiskCacheBuffer;
+ try
+ {
+ mDiskCacheBuffer = new U8[size];
+ }
+ catch (std::bad_alloc&)
+ {
+ LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh thread's buffer, size: " << size << LL_ENDL;
+ mDiskCacheBuffer = NULL;
+
+ // Not sure what size is reasonable
+ // but if 30MB allocation failed, we definitely have issues
+ const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
+ if (size < MAX_SIZE)
+ {
+ LLAppViewer::instance()->outOfMemorySoftQuit();
+ } // else ignore failures for anomalously large data
+ }
+ mDiskCacheBufferSize = size;
+ }
+ else
+ {
+ // reusing old buffer, reset heading bytes to ensure
+ // old content won't be parsable if something fails.
+ memset(mDiskCacheBuffer, 0, 16);
+ }
+ return mDiskCacheBuffer;
+}
+
// Mutex: must be holding mMutex when called
void LLMeshRepoThread::setGetMeshCap(const std::string & mesh_cap)
{
@@ -1327,7 +1504,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url,
}
-bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
{
LL_PROFILE_ZONE_SCOPED;
if (!mHeaderMutex)
@@ -1337,7 +1514,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
mHeaderMutex->lock();
- auto header_it = mMeshHeader.find(mesh_id);
+ mesh_header_map::const_iterator header_it = mMeshHeader.find(mesh_id);
if (header_it == mMeshHeader.end())
{ //we have no header info for this mesh, do nothing
mHeaderMutex->unlock();
@@ -1346,23 +1523,24 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
++LLMeshRepository::sMeshRequestCount;
bool ret = true;
- U32 header_size = header_it->second.first;
+ const LLMeshHeader& header = header_it->second;
+ U32 header_size = header.mHeaderSize;
if (header_size > 0)
{
- const LLMeshHeader& header = header_it->second.second;
-
S32 version = header.mVersion;
S32 offset = header_size + header.mSkinOffset;
S32 size = header.mSkinSize;
+ bool in_cache = header.mSkinInCache;
mHeaderMutex->unlock();
if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)
{
//check cache for mesh skin info
+ S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;
LLFileSystem file(mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset + size)
+ if (in_cache && file.getSize() >= disk_ofset + size)
{
U8* buffer = new(std::nothrow) U8[size];
if (!buffer)
@@ -1370,19 +1548,19 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL;
// Not sure what size is reasonable for skin info,
- // but if 20MB allocation failed, we definetely have issues
+ // but if 30MB allocation failed, we definitely have issues
const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
if (size < MAX_SIZE)
{
LLAppViewer::instance()->outOfMemorySoftQuit();
} // else ignore failures for anomalously large data
- LLMutexLock locker(mMutex);
+ LLMutexLock locker(mLoadedMutex);
mSkinUnavailableQ.emplace_back(mesh_id);
return true;
}
LLMeshRepository::sCacheBytesRead += size;
++LLMeshRepository::sCacheReads;
- file.seek(offset);
+ file.seek(disk_ofset);
file.read(buffer, size);
//make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written)
@@ -1393,14 +1571,72 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
}
if (!zero)
- { //attempt to parse
- if (skinInfoReceived(mesh_id, buffer, size))
+ {
+ //attempt to parse
+ bool posted = mMeshThreadPool->getQueue().post(
+ [mesh_id, buffer, size]
+ ()
+ {
+ if (gMeshRepo.mThread->isShuttingDown())
+ {
+ delete[] buffer;
+ return;
+ }
+ if (!gMeshRepo.mThread->skinInfoReceived(mesh_id, buffer, size))
+ {
+ // either header is faulty or something else overwrote the cache
+ S32 header_size = 0;
+ U32 header_flags = 0;
+ {
+ LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL;
+
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // for safety just mark everything as missing
+ header.mSkinInCache = false;
+ header.mPhysicsConvexInCache = false;
+ header.mPhysicsMeshInCache = false;
+ for (S32 i = 0; i < LLModel::NUM_LODS; ++i)
+ {
+ header.mLodInCache[i] = false;
+ }
+ header_size = header.mHeaderSize;
+ header_flags = header.getFlags();
+ }
+ }
+
+ if (header_size > 0)
+ {
+ LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
+ if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE)
+ {
+ write_preamble(file, header_size, header_flags);
+ }
+ }
+
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ UUIDBasedRequest req(mesh_id);
+ gMeshRepo.mThread->mSkinRequests.push_back(req);
+ }
+ }
+ delete[] buffer;
+ });
+ if (posted)
+ {
+ // lambda owns buffer
+ return true;
+ }
+ else if (skinInfoReceived(mesh_id, buffer, size))
{
delete[] buffer;
return true;
}
}
-
delete[] buffer;
}
@@ -1410,7 +1646,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
if (!http_url.empty())
{
- LLMeshHandlerBase::ptr_t handler(new LLMeshSkinInfoHandler(mesh_id, offset, size));
+ LLMeshHandlerBase::ptr_t handler = std::make_shared<LLMeshSkinInfoHandler>(mesh_id, offset, size);
LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
@@ -1420,26 +1656,21 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
<< LL_ENDL;
ret = false;
}
- else if(can_retry)
+ else
{
handler->mHttpHandle = handle;
mHttpRequestSet.insert(handler);
}
- else
- {
- LLMutexLock locker(mMutex);
- mSkinUnavailableQ.emplace_back(mesh_id);
- }
}
else
{
- LLMutexLock locker(mMutex);
+ LLMutexLock locker(mLoadedMutex);
mSkinUnavailableQ.emplace_back(mesh_id);
}
}
else
{
- LLMutexLock locker(mMutex);
+ LLMutexLock locker(mLoadedMutex);
mSkinUnavailableQ.emplace_back(mesh_id);
}
}
@@ -1470,42 +1701,35 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
}
++LLMeshRepository::sMeshRequestCount;
- U32 header_size = header_it->second.first;
+ const auto& header = header_it->second;
+ U32 header_size = header.mHeaderSize;
bool ret = true;
if (header_size > 0)
{
- const auto& header = header_it->second.second;
S32 version = header.mVersion;
S32 offset = header_size + header.mPhysicsConvexOffset;
S32 size = header.mPhysicsConvexSize;
+ bool in_cache = header.mPhysicsConvexInCache;
mHeaderMutex->unlock();
if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)
{
- //check cache for mesh skin info
+ // check cache for mesh decomposition
+ S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;
LLFileSystem file(mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
+ if (in_cache && file.getSize() >= disk_ofset + size)
{
- U8* buffer = new(std::nothrow) U8[size];
+ U8* buffer = getDiskCacheBuffer(size);
if (!buffer)
{
- LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL;
-
- // Not sure what size is reasonable for decomposition
- // but if 20MB allocation failed, we definetely have issues
- const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
- if (size < MAX_SIZE)
- {
- LLAppViewer::instance()->outOfMemorySoftQuit();
- } // else ignore failures for anomalously large decompositiions
return true;
}
LLMeshRepository::sCacheBytesRead += size;
++LLMeshRepository::sCacheReads;
- file.seek(offset);
+ file.seek(disk_ofset);
file.read(buffer, size);
//make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written)
@@ -1519,12 +1743,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
{ //attempt to parse
if (decompositionReceived(mesh_id, buffer, size))
{
- delete[] buffer;
return true;
}
}
-
- delete[] buffer;
}
//reading from cache failed for whatever reason, fetch from sim
@@ -1533,7 +1754,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
if (!http_url.empty())
{
- LLMeshHandlerBase::ptr_t handler(new LLMeshDecompositionHandler(mesh_id, offset, size));
+ LLMeshHandlerBase::ptr_t handler = std::make_shared<LLMeshDecompositionHandler>(mesh_id, offset, size);
LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
@@ -1578,41 +1799,36 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
}
++LLMeshRepository::sMeshRequestCount;
- U32 header_size = header_it->second.first;
+ const auto& header = header_it->second;
+ U32 header_size = header.mHeaderSize;
bool ret = true;
if (header_size > 0)
{
- const auto& header = header_it->second.second;
S32 version = header.mVersion;
S32 offset = header_size + header.mPhysicsMeshOffset;
S32 size = header.mPhysicsMeshSize;
+ bool in_cache = header.mPhysicsMeshInCache;
mHeaderMutex->unlock();
+ // todo: check header.mHasPhysicsMesh
if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)
{
//check cache for mesh physics shape info
+ S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;
LLFileSystem file(mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
+ if (in_cache && file.getSize() >= disk_ofset +size)
{
LLMeshRepository::sCacheBytesRead += size;
++LLMeshRepository::sCacheReads;
- file.seek(offset);
- U8* buffer = new(std::nothrow) U8[size];
+
+ U8* buffer = getDiskCacheBuffer(size);
if (!buffer)
{
- LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL;
-
- // Not sure what size is reasonable for physcis
- // but if 20MB allocation failed, we definetely have issues
- const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
- if (size < MAX_SIZE)
- {
- LLAppViewer::instance()->outOfMemorySoftQuit();
- } // else ignore failures for anomalously large data
return true;
}
+ file.seek(disk_ofset);
file.read(buffer, size);
//make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written)
@@ -1626,12 +1842,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
{ //attempt to parse
if (physicsShapeReceived(mesh_id, buffer, size) == MESH_OK)
{
- delete[] buffer;
return true;
}
}
-
- delete[] buffer;
}
//reading from cache failed for whatever reason, fetch from sim
@@ -1640,7 +1853,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
if (!http_url.empty())
{
- LLMeshHandlerBase::ptr_t handler(new LLMeshPhysicsShapeHandler(mesh_id, offset, size));
+ LLMeshHandlerBase::ptr_t handler = std::make_shared<LLMeshPhysicsShapeHandler>(mesh_id, offset, size);
LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
@@ -1674,33 +1887,41 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
//static
void LLMeshRepoThread::incActiveLODRequests()
{
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
++LLMeshRepoThread::sActiveLODRequests;
}
//static
void LLMeshRepoThread::decActiveLODRequests()
{
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
--LLMeshRepoThread::sActiveLODRequests;
}
//static
void LLMeshRepoThread::incActiveHeaderRequests()
{
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
++LLMeshRepoThread::sActiveHeaderRequests;
}
//static
void LLMeshRepoThread::decActiveHeaderRequests()
{
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
--LLMeshRepoThread::sActiveHeaderRequests;
}
+//static
+void LLMeshRepoThread::incActiveSkinRequests()
+{
+ ++LLMeshRepoThread::sActiveSkinRequests;
+}
+
+//static
+void LLMeshRepoThread::decActiveSkinRequests()
+{
+ --LLMeshRepoThread::sActiveSkinRequests;
+}
+
//return false if failed to get header
-bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry)
+bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
{
LL_PROFILE_ZONE_SCOPED;
++LLMeshRepository::sMeshRequestCount;
@@ -1714,17 +1935,34 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c
if (size > 0)
{
// *NOTE: if the header size is ever more than 4KB, this will break
- U8 buffer[MESH_HEADER_SIZE];
- S32 bytes = llmin(size, MESH_HEADER_SIZE);
+ constexpr S32 DISK_MINIMAL_READ = 4096;
+ U8 buffer[DISK_MINIMAL_READ * 2];
+ S32 bytes = llmin(size, DISK_MINIMAL_READ);
LLMeshRepository::sCacheBytesRead += bytes;
++LLMeshRepository::sCacheReads;
+
file.read(buffer, bytes);
- if (headerReceived(mesh_params, buffer, bytes) == MESH_OK)
+
+ U32 version = 0;
+ memcpy(&version, buffer, sizeof(U32));
+ if (version == CACHE_PREAMBLE_VERSION)
{
- LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL;
+ S32 header_size = 0;
+ memcpy(&header_size, buffer + sizeof(U32), sizeof(S32));
+ if (header_size + CACHE_PREAMBLE_SIZE > DISK_MINIMAL_READ)
+ {
+ bytes = llmin(size , DISK_MINIMAL_READ * 2);
+ file.read(buffer + DISK_MINIMAL_READ, bytes - DISK_MINIMAL_READ);
+ }
+ U32 flags = 0;
+ memcpy(&flags, buffer + 2 * sizeof(U32), sizeof(U32));
+ if (headerReceived(mesh_params, buffer + CACHE_PREAMBLE_SIZE, bytes - CACHE_PREAMBLE_SIZE, flags) == MESH_OK)
+ {
+ LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL;
- // Found mesh in cache
- return true;
+ // Found mesh in cache
+ return true;
+ }
}
}
}
@@ -1743,7 +1981,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c
//within the first 4KB
//NOTE -- this will break of headers ever exceed 4KB
- LLMeshHandlerBase::ptr_t handler(new LLMeshHeaderHandler(mesh_params, 0, MESH_HEADER_SIZE));
+ LLMeshHandlerBase::ptr_t handler = std::make_shared<LLMeshHeaderHandler>(mesh_params, 0, MESH_HEADER_SIZE);
LLCore::HttpHandle handle = getByteRange(http_url, 0, MESH_HEADER_SIZE, handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
@@ -1753,7 +1991,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c
<< LL_ENDL;
retval = false;
}
- else if (can_retry)
+ else
{
handler->mHttpHandle = handle;
mHttpRequestSet.insert(handler);
@@ -1764,7 +2002,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)
+bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
{
LL_PROFILE_ZONE_SCOPED;
if (!mHeaderMutex)
@@ -1784,41 +2022,43 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
++LLMeshRepository::sMeshRequestCount;
bool retval = true;
- U32 header_size = header_it->second.first;
+ const auto& header = header_it->second;
+ U32 header_size = header.mHeaderSize;
if (header_size > 0)
{
- const auto& header = header_it->second.second;
S32 version = header.mVersion;
S32 offset = header_size + header.mLodOffset[lod];
S32 size = header.mLodSize[lod];
+ bool in_cache = header.mLodInCache[lod];
mHeaderMutex->unlock();
if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)
{
-
+ S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;
//check cache for mesh asset
LLFileSystem file(mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
+ if (in_cache && (file.getSize() >= disk_ofset + size))
{
- U8* buffer = new(std::nothrow) U8[size];
+ U8* buffer = new(std::nothrow) U8[size]; // todo, make buffer thread local and read in thread?
if (!buffer)
{
LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL;
// Not sure what size is reasonable for a mesh,
- // but if 20MB allocation failed, we definetely have issues
+ // but if 30MB allocation failed, we definitely have issues
const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB
if (size < MAX_SIZE)
{
LLAppViewer::instance()->outOfMemorySoftQuit();
} // else ignore failures for anomalously large data
- LLMutexLock lock(mMutex);
+
+ LLMutexLock lock(mLoadedMutex);
mUnavailableQ.push_back(LODRequest(mesh_params, lod));
return true;
}
LLMeshRepository::sCacheBytesRead += size;
++LLMeshRepository::sCacheReads;
- file.seek(offset);
+ file.seek(disk_ofset);
file.read(buffer, size);
//make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written)
@@ -1829,15 +2069,81 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
}
if (!zero)
- { //attempt to parse
- if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK)
+ {
+ //attempt to parse
+ const LLVolumeParams params(mesh_params);
+ bool posted = mMeshThreadPool->getQueue().post(
+ [params, mesh_id, lod, buffer, size]
+ ()
{
+ if (gMeshRepo.mThread->isShuttingDown())
+ {
+ delete[] buffer;
+ return;
+ }
+ if (gMeshRepo.mThread->lodReceived(params, lod, buffer, size) == MESH_OK)
+ {
+ LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL;
+ }
+ else
+ {
+ // either header is faulty or something else overwrote the cache
+ S32 header_size = 0;
+ U32 header_flags = 0;
+ {
+ LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL;
+
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // for safety just mark everything as missing
+ header.mSkinInCache = false;
+ header.mPhysicsConvexInCache = false;
+ header.mPhysicsMeshInCache = false;
+ for (S32 i = 0; i < LLModel::NUM_LODS; ++i)
+ {
+ header.mLodInCache[i] = false;
+ }
+ header_size = header.mHeaderSize;
+ header_flags = header.getFlags();
+ }
+ }
+
+ if (header_size > 0)
+ {
+ LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
+ if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE)
+ {
+ write_preamble(file, header_size, header_flags);
+ }
+ }
+
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LODRequest req(params, lod);
+ gMeshRepo.mThread->mLODReqQ.push(req);
+ LLMeshRepository::sLODProcessing++;
+ }
+ }
delete[] buffer;
+ });
+ if (posted)
+ {
+ // now lambda owns buffer
+ return true;
+ }
+ else if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK)
+ {
+ delete[] buffer;
LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL;
return true;
}
+
}
delete[] buffer;
@@ -1851,7 +2157,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
{
LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the simulator." << LL_ENDL;
- LLMeshHandlerBase::ptr_t handler(new LLMeshLODHandler(mesh_params, lod, offset, size));
+ LLMeshHandlerBase::ptr_t handler = std::make_shared<LLMeshLODHandler>(mesh_params, lod, offset, size);
LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
@@ -1861,27 +2167,22 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
<< LL_ENDL;
retval = false;
}
- else if (can_retry)
+ else
{
+ // we already made a request, store the handle
handler->mHttpHandle = handle;
mHttpRequestSet.insert(handler);
- // *NOTE: Allowing a re-request, not marking as unavailable. Is that correct?
- }
- else
- {
- LLMutexLock lock(mMutex);
- mUnavailableQ.push_back(LODRequest(mesh_params, lod));
}
}
else
{
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mUnavailableQ.push_back(LODRequest(mesh_params, lod));
}
}
else
{
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mUnavailableQ.push_back(LODRequest(mesh_params, lod));
}
}
@@ -1893,14 +2194,19 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
return retval;
}
-EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size)
+EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags)
{
+ LL_PROFILE_ZONE_SCOPED;
const LLUUID mesh_id = mesh_params.getSculptID();
LLSD header_data;
LLMeshHeader header;
llssize header_size = 0;
+ S32 skin_offset = -1;
+ S32 skin_size = -1;
+ S32 lod_offset[LLModel::NUM_LODS] = { -1 };
+ S32 lod_size[LLModel::NUM_LODS] = { -1 };
if (data_size > 0)
{
llssize dsize = data_size;
@@ -1933,7 +2239,40 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes
// make sure there is at least one lod, function returns -1 and marks as 404 otherwise
else if (LLMeshRepository::getActualMeshLOD(header, 0) >= 0)
{
- header_size += stream.tellg();
+ header.mHeaderSize = (S32)stream.tellg();
+ header_size += header.mHeaderSize;
+ skin_offset = header.mSkinOffset;
+ skin_size = header.mSkinSize;
+
+ memcpy(lod_offset, header.mLodOffset, sizeof(lod_offset));
+ memcpy(lod_size, header.mLodSize, sizeof(lod_size));
+
+ if (flags != 0)
+ {
+ header.setFromFlags(flags);
+ }
+ else
+ {
+ if (header.mSkinSize > 0 && header_size + header.mSkinOffset + header.mSkinSize < data_size)
+ {
+ header.mSkinInCache = true;
+ }
+ if (header.mPhysicsConvexSize > 0 && header_size + header.mPhysicsConvexOffset + header.mPhysicsConvexSize < data_size)
+ {
+ header.mPhysicsConvexInCache = true;
+ }
+ if (header.mPhysicsMeshSize > 0 && header_size + header.mPhysicsMeshOffset + header.mPhysicsMeshSize < data_size)
+ {
+ header.mPhysicsMeshInCache = true;
+ }
+ for (S32 i = 0; i < LLModel::NUM_LODS; ++i)
+ {
+ if (lod_size[i] > 0 && header_size + lod_offset[i] + lod_size[i] < data_size)
+ {
+ header.mLodInCache[i] = true;
+ }
+ }
+ }
}
}
else
@@ -1947,35 +2286,85 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes
{
LLMutexLock lock(mHeaderMutex);
- mMeshHeader[mesh_id] = { (U32)header_size, header };
+ mMeshHeader[mesh_id] = header;
LLMeshRepository::sCacheBytesHeaders += (U32)header_size;
}
// immediately request SkinInfo since we'll need it before we can render any LoD if it is present
+ if (skin_offset >= 0 && skin_size > 0)
{
- LLMutexLock lock(gMeshRepo.mMeshMutex);
+ {
+ LLMutexLock lock(gMeshRepo.mMeshMutex);
- if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end())
+ 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
+ }
+ }
+
+ S32 offset = (S32)header_size + skin_offset;
+ bool request_skin = true;
+ if (offset + skin_size < data_size)
{
- gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info
+ request_skin = !skinInfoReceived(mesh_id, data + offset, skin_size);
+ }
+ if (request_skin)
+ {
+ LLMutexLock lock(mMutex);
+ mSkinRequests.push_back(UUIDBasedRequest(mesh_id));
}
}
- fetchMeshSkinInfo(mesh_id);
-
- LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time.
+ std::array<S32, LLModel::NUM_LODS> pending_lods;
+ bool has_pending_lods = false;
+ {
+ LLMutexLock lock(mPendingMutex); // make sure only one thread access mPendingLOD at the same time.
+ pending_lod_map::iterator iter = mPendingLOD.find(mesh_id);
+ if (iter != mPendingLOD.end())
+ {
+ pending_lods = iter->second;
+ mPendingLOD.erase(iter);
+ has_pending_lods = true;
+ }
+ }
//check for pending requests
- pending_lod_map::iterator iter = mPendingLOD.find(mesh_id);
- if (iter != mPendingLOD.end())
+ if (has_pending_lods)
{
- for (U32 i = 0; i < iter->second.size(); ++i)
+ for (S32 i = 0; i < pending_lods.size(); ++i)
{
- LODRequest req(mesh_params, iter->second[i]);
- mLODReqQ.push(req);
- LLMeshRepository::sLODProcessing++;
+ if (pending_lods[i] > 1)
+ {
+ // mLoadingMeshes should be protecting from dupplciates, but looks
+ // like this is possible if object rezzes, unregisterMesh, then
+ // rezzes again before first request completes.
+ // mLoadingMeshes might need to change a bit to not rerequest if
+ // mesh is already pending.
+ //
+ // Todo: Improve mLoadingMeshes and once done turn this into an assert.
+ // Low priority since such situation should be relatively rare
+ LL_INFOS(LOG_MESH) << "Multiple dupplicate requests for mesd ID: " << mesh_id << " LOD: " << i
+ << LL_ENDL;
+ }
+ if (pending_lods[i] > 0 && lod_size[i] > 0)
+ {
+ // try to load from data we just received
+ bool request_lod = true;
+ S32 offset = (S32)header_size + lod_offset[i];
+ if (offset + lod_size[i] <= data_size)
+ {
+ // initial request is 4096 bytes, it's big enough to fit this lod
+ request_lod = lodReceived(mesh_params, i, data + offset, lod_size[i]) != MESH_OK;
+ }
+ if (request_lod)
+ {
+ LLMutexLock lock(mMutex);
+ LODRequest req(mesh_params, i);
+ mLODReqQ.push(req);
+ LLMeshRepository::sLODProcessing++;
+ }
+ }
}
- mPendingLOD.erase(iter);
}
}
@@ -1992,13 +2381,27 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
LLPointer<LLVolume> volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod));
if (volume->unpackVolumeFaces(data, data_size))
{
- if (volume->getNumFaces() > 0)
+ // Use LLVolume::getNumVolumeFaces() here and not LLVolume::getNumFaces(),
+ // because setMeshAssetLoaded() has not yet been called for this volume
+ // (it is set later in LLMeshRepository::notifyMeshLoaded()), and
+ // getNumFaces() would return the number of faces in the LLProfile
+ // instead. HB
+ S32 num_faces = volume->getNumVolumeFaces();
+ if (num_faces > 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())
+ LLPointer<LLMeshSkinInfo> skin_info = nullptr;
{
- for (S32 i = 0; i < volume->getNumFaces(); ++i)
+ LLMutexLock lock(mSkinMapMutex);
+ skin_map::iterator iter = mSkinMap.find(mesh_params.getSculptID());
+ if (iter != mSkinMap.end())
+ {
+ skin_info = iter->second;
+ }
+ }
+ if (skin_info.notNull() && isAgentAvatarValid())
+ {
+ for (S32 i = 0; i < num_faces; ++i)
{
// NOTE: no need to lock gAgentAvatarp as the state being checked is not changed after initialization
LLVolumeFace& face = volume->getVolumeFace(i);
@@ -2008,7 +2411,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
LoadedMesh mesh(volume, mesh_params, lod);
{
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mLoadedQ.push_back(mesh);
// LLPointer is not thread safe, since we added this pointer into
// threaded list, make sure counter gets decreased inside mutex lock
@@ -2017,6 +2420,11 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
// might be good idea to turn mesh into pointer to avoid making a copy
mesh.mVolume = NULL;
}
+ {
+ // make sure skin info is not removed from list while we are decreasing reference count
+ LLMutexLock lock(mSkinMapMutex);
+ skin_info = nullptr;
+ }
return MESH_OK;
}
}
@@ -2026,6 +2434,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
LLSD skin;
if (data_size > 0)
@@ -2052,7 +2461,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
LLPointer<LLMeshSkinInfo> info = nullptr;
info = new LLMeshSkinInfo(mesh_id, skin);
- if (isAgentAvatarValid())
+ if (isAgentAvatarValid() && gAgentAvatarp->mInitFlags != 0)
{ // 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);
@@ -2060,12 +2469,15 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
// copy the skin info for the background thread so we can use it
// to calculate per-joint bounding boxes when volumes are loaded
- mSkinMap[mesh_id] = new LLMeshSkinInfo(*info);
+ {
+ LLMutexLock lock(mSkinMapMutex);
+ mSkinMap[mesh_id] = new LLMeshSkinInfo(*info);
+ }
{
// Move the LLPointer in to the skin info queue to avoid reference
// count modification after we leave the lock
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mSkinInfoQ.emplace_back(std::move(info));
}
}
@@ -2075,6 +2487,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
LLSD decomp;
if (data_size > 0)
@@ -2101,7 +2514,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
LLModel::Decomposition* d = new LLModel::Decomposition(decomp);
d->mMeshID = mesh_id;
{
- LLMutexLock lock(mMutex);
+ LLMutexLock lock(mLoadedMutex);
mDecompositionQ.push_back(d);
}
}
@@ -2111,6 +2524,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
LLSD physics_shape;
LLModel::Decomposition* d = new LLModel::Decomposition();
@@ -2150,15 +2564,16 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_
}
{
- LLMutexLock lock(mMutex);
- mDecompositionQ.push_back(d);
+ LLMutexLock lock(mLoadedMutex);
+ mPhysicsQ.push_back(d);
}
return MESH_OK;
}
-LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures,
+LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list_t& data, const LLMeshUploadThread::lod_sources_map_t& sources_list,
+ LLVector3& scale, bool upload_textures,
bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position,
- const std::string & upload_url, bool do_upload,
+ const std::string & upload_url, LLUUID destination_folder_id, bool do_upload,
LLHandle<LLWholeModelFeeObserver> fee_observer,
LLHandle<LLWholeModelUploadObserver> upload_observer)
: LLThread("mesh upload"),
@@ -2166,10 +2581,12 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
mDiscarded(false),
mDoUpload(do_upload),
mWholeModelUploadURL(upload_url),
+ mDestinationFolderId(destination_folder_id),
mFeeObserverHandle(fee_observer),
mUploadObserverHandle(upload_observer)
{
mInstanceList = data;
+ mLodSources = sources_list;
mUploadTextures = upload_textures;
mUploadSkin = upload_skin;
mUploadJoints = upload_joints;
@@ -2187,11 +2604,11 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut");
mHttpRequest = new LLCore::HttpRequest;
- mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
+ mHttpOptions = std::make_shared<LLCore::HttpOptions>();
mHttpOptions->setTransferTimeout(mMeshUploadTimeOut);
mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter"));
mHttpOptions->setRetries(UPLOAD_RETRY_LIMIT);
- mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders);
+ mHttpHeaders = std::make_shared<LLCore::HttpHeaders>();
mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML);
mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS);
}
@@ -2202,7 +2619,6 @@ LLMeshUploadThread::~LLMeshUploadThread()
mHttpRequest = NULL;
delete mMutex;
mMutex = NULL;
-
}
LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread)
@@ -2236,7 +2652,7 @@ void LLMeshUploadThread::DecompRequest::completed()
void LLMeshUploadThread::preStart()
{
//build map of LLModel refs to instances for callbacks
- for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter)
+ for (instance_list_t::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter)
{
mInstance[iter->mModel].push_back(*iter);
}
@@ -2270,6 +2686,8 @@ void dump_llsd_to_file(const LLSD& content, std::string filename)
{
if (gSavedSettings.getBOOL("MeshUploadLogXML"))
{
+ filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
+ filename);
llofstream of(filename.c_str());
LLSDSerialize::toPrettyXML(content,of);
}
@@ -2283,346 +2701,302 @@ LLSD llsd_from_file(std::string filename)
return result;
}
-void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
+void LLMeshUploadThread::packModelIntance(
+ LLModel* model,
+ LLMeshUploadThread::instance_list_t& instance_list,
+ std::string& model_name,
+ LLSD& res,
+ S32& mesh_num,
+ S32& texture_num,
+ S32& instance_num,
+ std::unordered_set<LLViewerTexture* >& textures,
+ std::unordered_map<LLViewerTexture*, S32> texture_index,
+ std::unordered_map<LLModel*, S32>& mesh_index,
+ std::vector<std::string>& texture_list_dest,
+ bool include_textures
+ )
{
- LLSD result;
-
- LLSD res;
- result["folder_id"] = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_OBJECT);
- result["texture_folder_id"] = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE);
- result["asset_type"] = "mesh";
- result["inventory_type"] = "object";
- result["description"] = "(No Description)";
- result["next_owner_mask"] = LLSD::Integer(LLFloaterPerms::getNextOwnerPerms("Uploads"));
- result["group_mask"] = LLSD::Integer(LLFloaterPerms::getGroupPerms("Uploads"));
- result["everyone_mask"] = LLSD::Integer(LLFloaterPerms::getEveryonePerms("Uploads"));
+ LLMeshUploadData data;
+ data.mBaseModel = model;
- res["mesh_list"] = LLSD::emptyArray();
- res["texture_list"] = LLSD::emptyArray();
- res["instance_list"] = LLSD::emptyArray();
- S32 mesh_num = 0;
- S32 texture_num = 0;
-
- std::unordered_set<LLViewerTexture* > textures;
- std::unordered_map<LLViewerTexture*,S32> texture_index;
-
- std::unordered_map<LLModel*,S32> mesh_index;
- std::string model_name;
-
- S32 instance_num = 0;
-
- for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ LLModelInstance& first_instance = *(instance_list.begin());
+ for (S32 i = 0; i < 5; i++)
{
- LLMeshUploadData data;
- data.mBaseModel = iter->first;
+ data.mModel[i] = first_instance.mLOD[i];
+ }
- if (data.mBaseModel->mSubmodelID)
+ if (mesh_index.find(data.mBaseModel) == mesh_index.end())
+ {
+ // Have not seen this model before - create a new mesh_list entry for it.
+ if (model_name.empty())
{
- // These are handled below to insure correct parenting order on creation
- // due to map walking being based on model address (aka random)
- continue;
+ model_name = data.mBaseModel->getName();
}
- LLModelInstance& first_instance = *(iter->second.begin());
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = first_instance.mLOD[i];
- }
+ std::stringstream ostr;
- if (mesh_index.find(data.mBaseModel) == mesh_index.end())
- {
- // Have not seen this model before - create a new mesh_list entry for it.
- if (model_name.empty())
- {
- model_name = data.mBaseModel->getName();
- }
+ LLModel::Decomposition& decomp =
+ data.mModel[LLModel::LOD_PHYSICS].notNull() ?
+ data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
+ data.mBaseModel->mPhysics;
- std::stringstream ostr;
+ decomp.mBaseHull = mHullMap[data.mBaseModel];
- LLModel::Decomposition& decomp =
- data.mModel[LLModel::LOD_PHYSICS].notNull() ?
- data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
- data.mBaseModel->mPhysics;
+ LLSD mesh_header = LLModel::writeModel(
+ ostr,
+ data.mModel[LLModel::LOD_PHYSICS],
+ data.mModel[LLModel::LOD_HIGH],
+ data.mModel[LLModel::LOD_MEDIUM],
+ data.mModel[LLModel::LOD_LOW],
+ data.mModel[LLModel::LOD_IMPOSTOR],
+ decomp,
+ mUploadSkin,
+ mUploadJoints,
+ mLockScaleIfJointPosition,
+ LLModel::WRITE_BINARY,
+ false,
+ data.mBaseModel->mSubmodelID);
- decomp.mBaseHull = mHullMap[data.mBaseModel];
+ data.mAssetData = ostr.str();
+ std::string str = ostr.str();
- LLSD mesh_header = LLModel::writeModel(
- ostr,
- data.mModel[LLModel::LOD_PHYSICS],
- data.mModel[LLModel::LOD_HIGH],
- data.mModel[LLModel::LOD_MEDIUM],
- data.mModel[LLModel::LOD_LOW],
- data.mModel[LLModel::LOD_IMPOSTOR],
- decomp,
- mUploadSkin,
- mUploadJoints,
- mLockScaleIfJointPosition,
- false,
- false,
- data.mBaseModel->mSubmodelID);
+ res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(), str.end());
+ mesh_index[data.mBaseModel] = mesh_num;
+ mesh_num++;
+ }
- data.mAssetData = ostr.str();
- std::string str = ostr.str();
+ // For all instances that use this model
+ for (instance_list_t::iterator instance_iter = instance_list.begin();
+ instance_iter != instance_list.end();
+ ++instance_iter)
+ {
+ LLModelInstance& instance = *instance_iter;
- res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end());
- mesh_index[data.mBaseModel] = mesh_num;
- mesh_num++;
- }
+ LLSD instance_entry;
- // For all instances that use this model
- for (instance_list::iterator instance_iter = iter->second.begin();
- instance_iter != iter->second.end();
- ++instance_iter)
+ for (S32 i = 0; i < 5; i++)
{
+ data.mModel[i] = instance.mLOD[i];
+ }
- LLModelInstance& instance = *instance_iter;
-
- LLSD instance_entry;
+ LLVector3 pos, scale;
+ LLQuaternion rot;
+ LLMatrix4 transformation = instance.mTransform;
+ decomposeMeshMatrix(transformation, pos, rot, scale);
+ instance_entry["position"] = ll_sd_from_vector3(pos);
+ instance_entry["rotation"] = ll_sd_from_quaternion(rot);
+ instance_entry["scale"] = ll_sd_from_vector3(scale);
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = instance.mLOD[i];
- }
+ instance_entry["material"] = LL_MCODE_WOOD;
+ if (model->mSubmodelID)
+ {
+ // Should it really be different?
+ instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_NONE);
+ }
+ else
+ {
+ instance_entry["physics_shape_type"] = data.mModel[LLModel::LOD_PHYSICS].notNull() ? (U8)(LLViewerObject::PHYSICS_SHAPE_PRIM) : (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
+ }
+ instance_entry["mesh"] = mesh_index[data.mBaseModel];
+ instance_entry["mesh_name"] = instance.mLabel;
- LLVector3 pos, scale;
- LLQuaternion rot;
- LLMatrix4 transformation = instance.mTransform;
- decomposeMeshMatrix(transformation,pos,rot,scale);
- instance_entry["position"] = ll_sd_from_vector3(pos);
- instance_entry["rotation"] = ll_sd_from_quaternion(rot);
- instance_entry["scale"] = ll_sd_from_vector3(scale);
+ instance_entry["face_list"] = LLSD::emptyArray();
- instance_entry["material"] = LL_MCODE_WOOD;
- instance_entry["physics_shape_type"] = data.mModel[LLModel::LOD_PHYSICS].notNull() ? (U8)(LLViewerObject::PHYSICS_SHAPE_PRIM) : (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
- instance_entry["mesh"] = mesh_index[data.mBaseModel];
- instance_entry["mesh_name"] = instance.mLabel;
+ // We want to be able to allow more than 8 materials...
+ //
+ S32 end = llmin((S32)data.mBaseModel->mMaterialList.size(), instance.mModel->getNumVolumeFaces());
- instance_entry["face_list"] = LLSD::emptyArray();
+ for (S32 face_num = 0; face_num < end; face_num++)
+ {
+ // multiple faces can reuse the same material
+ LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]];
+ LLSD face_entry = LLSD::emptyMap();
- // We want to be able to allow more than 8 materials...
- //
- S32 end = llmin((S32)data.mBaseModel->mMaterialList.size(), instance.mModel->getNumVolumeFaces()) ;
+ LLViewerFetchedTexture* texture = NULL;
- for (S32 face_num = 0; face_num < end; face_num++)
+ if (material.mDiffuseMapFilename.size())
{
- // multiple faces can reuse the same material
- LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]];
- LLSD face_entry = LLSD::emptyMap();
+ texture = FindViewerTexture(material);
+ }
- LLViewerFetchedTexture *texture = NULL;
+ if ((texture != NULL) &&
+ (textures.find(texture) == textures.end()))
+ {
+ textures.insert(texture);
+ }
- if (material.mDiffuseMapFilename.size())
+ std::stringstream texture_str;
+ if (texture != NULL && include_textures && mUploadTextures)
+ {
+ if (texture->hasSavedRawImage())
{
- texture = FindViewerTexture(material);
- }
+ LLImageDataLock lock(texture->getSavedRawImage());
- if ((texture != NULL) &&
- (textures.find(texture) == textures.end()))
- {
- textures.insert(texture);
- }
+ LLPointer<LLImageJ2C> upload_file =
+ LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage());
- std::stringstream texture_str;
- if (texture != NULL && include_textures && mUploadTextures)
- {
- if (texture->hasSavedRawImage())
+ if (!upload_file.isNull() && upload_file->getDataSize() && !upload_file->isBufferInvalid())
{
- LLImageDataLock lock(texture->getSavedRawImage());
-
- LLPointer<LLImageJ2C> upload_file =
- LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage());
-
- if (!upload_file.isNull() && upload_file->getDataSize())
- {
- texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize());
- }
+ texture_str.write((const char*)upload_file->getData(), upload_file->getDataSize());
}
}
+ }
- if (texture != NULL &&
- mUploadTextures &&
- texture_index.find(texture) == texture_index.end())
- {
- texture_index[texture] = texture_num;
- std::string str = texture_str.str();
- res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end());
- texture_num++;
- }
-
- // Subset of TextureEntry fields.
- if (texture != NULL && mUploadTextures)
- {
- face_entry["image"] = texture_index[texture];
- face_entry["scales"] = 1.0;
- face_entry["scalet"] = 1.0;
- face_entry["offsets"] = 0.0;
- face_entry["offsett"] = 0.0;
- face_entry["imagerot"] = 0.0;
- }
- face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor);
- face_entry["fullbright"] = material.mFullbright;
- instance_entry["face_list"][face_num] = face_entry;
+ if (texture != NULL &&
+ mUploadTextures &&
+ texture_index.find(texture) == texture_index.end())
+ {
+ texture_index[texture] = texture_num;
+ std::string str = texture_str.str();
+ res["texture_list"][texture_num] = LLSD::Binary(str.begin(), str.end());
+ // store indexes for error handling;
+ texture_list_dest.push_back(material.mDiffuseMapFilename);
+ texture_num++;
}
- res["instance_list"][instance_num] = instance_entry;
- instance_num++;
+ // Subset of TextureEntry fields.
+ if (texture != NULL && mUploadTextures)
+ {
+ face_entry["image"] = texture_index[texture];
+ face_entry["scales"] = 1.0;
+ face_entry["scalet"] = 1.0;
+ face_entry["offsets"] = 0.0;
+ face_entry["offsett"] = 0.0;
+ face_entry["imagerot"] = 0.0;
+ }
+ face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor);
+ face_entry["fullbright"] = material.mFullbright;
+ instance_entry["face_list"][face_num] = face_entry;
}
+
+ res["instance_list"][instance_num] = instance_entry;
+ instance_num++;
+ }
+}
+
+void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>& texture_list_dest, bool include_textures)
+{
+ LLSD result;
+
+ LLSD res;
+ if (mDestinationFolderId.isNull())
+ {
+ result["folder_id"] = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_OBJECT);
+ result["texture_folder_id"] = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE);
}
+ else
+ {
+ result["folder_id"] = mDestinationFolderId;
+ result["texture_folder_id"] = mDestinationFolderId;
+ }
+ result["asset_type"] = "mesh";
+ result["inventory_type"] = "object";
+ result["description"] = "(No Description)";
+ result["next_owner_mask"] = LLSD::Integer(LLFloaterPerms::getNextOwnerPerms("Uploads"));
+ result["group_mask"] = LLSD::Integer(LLFloaterPerms::getGroupPerms("Uploads"));
+ result["everyone_mask"] = LLSD::Integer(LLFloaterPerms::getEveryonePerms("Uploads"));
- for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ res["mesh_list"] = LLSD::emptyArray();
+ res["texture_list"] = LLSD::emptyArray();
+ res["instance_list"] = LLSD::emptyArray();
+ LLSD& lod_sources = res["source_format"];
+ lod_sources["high"] = 0;
+ for (auto &source : mLodSources)
{
- LLMeshUploadData data;
- data.mBaseModel = iter->first;
+ lod_sources[source.first] = source.second;
+ }
+ S32 mesh_num = 0;
+ S32 texture_num = 0;
+ S32 instance_num = 0;
+
+ std::unordered_set<LLViewerTexture* > textures;
+ std::unordered_map<LLViewerTexture*,S32> texture_index;
- if (!data.mBaseModel->mSubmodelID)
+ std::unordered_map<LLModel*,S32> mesh_index;
+ std::string model_name;
+
+ // If server gets a m1, m2, m3, m4 list, m1 becomes the root
+ // and the rest go as m4, m3, m2
+ // to counter that mInstance is sorted as m4, m3, m2, m1
+ // and we grab m1 from the end and send it first
+ LLModel* root_model = nullptr;
+ for (instance_map_t::reverse_iterator iter = mInstance.rbegin(); iter != mInstance.rend(); ++iter)
+ {
+ if (iter->first->mSubmodelID)
{
- // These were handled above already...
- //
+ // Submodel can't be root
continue;
}
+ root_model = iter->first;
+ packModelIntance(
+ iter->first,
+ iter->second,
+ model_name,
+ res,
+ mesh_num,
+ texture_num,
+ instance_num,
+ textures,
+ texture_index,
+ mesh_index,
+ texture_list_dest,
+ include_textures);
+ break;
+ }
- LLModelInstance& first_instance = *(iter->second.begin());
- for (S32 i = 0; i < 5; i++)
+ // Handle models, ignore submodels for now.
+ // Probably should pre-sort by mSubmodelID instead of running twice.
+ // Note: mInstance should be sorted by model name for the sake of
+ // deterministic order.
+ for (auto& iter : mInstance)
+ {
+ if (iter.first->mSubmodelID)
{
- data.mModel[i] = first_instance.mLOD[i];
+ // These are handled below to insure correct parenting order on creation
+ // due to map walking being based on model address (aka random)
+ continue;
}
-
- if (mesh_index.find(data.mBaseModel) == mesh_index.end())
+ if (root_model == iter.first)
{
- // Have not seen this model before - create a new mesh_list entry for it.
- if (model_name.empty())
- {
- model_name = data.mBaseModel->getName();
- }
-
- std::stringstream ostr;
-
- LLModel::Decomposition& decomp =
- data.mModel[LLModel::LOD_PHYSICS].notNull() ?
- data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
- data.mBaseModel->mPhysics;
-
- decomp.mBaseHull = mHullMap[data.mBaseModel];
-
- LLSD mesh_header = LLModel::writeModel(
- ostr,
- data.mModel[LLModel::LOD_PHYSICS],
- data.mModel[LLModel::LOD_HIGH],
- data.mModel[LLModel::LOD_MEDIUM],
- data.mModel[LLModel::LOD_LOW],
- data.mModel[LLModel::LOD_IMPOSTOR],
- decomp,
- mUploadSkin,
- mUploadJoints,
- mLockScaleIfJointPosition,
- false,
- false,
- data.mBaseModel->mSubmodelID);
-
- data.mAssetData = ostr.str();
- std::string str = ostr.str();
-
- res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end());
- mesh_index[data.mBaseModel] = mesh_num;
- mesh_num++;
+ // Reached root, root was already packed and is last non-submodel
+ break;
}
+ packModelIntance(
+ iter.first,
+ iter.second,
+ model_name,
+ res,
+ mesh_num,
+ texture_num,
+ instance_num,
+ textures,
+ texture_index,
+ mesh_index,
+ texture_list_dest,
+ include_textures);
+ }
- // For all instances that use this model
- for (instance_list::iterator instance_iter = iter->second.begin();
- instance_iter != iter->second.end();
- ++instance_iter)
+ // Now handle the submodels.
+ for (auto& iter : mInstance)
+ {
+ if (!iter.first->mSubmodelID)
{
-
- LLModelInstance& instance = *instance_iter;
-
- LLSD instance_entry;
-
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = instance.mLOD[i];
- }
-
- LLVector3 pos, scale;
- LLQuaternion rot;
- LLMatrix4 transformation = instance.mTransform;
- decomposeMeshMatrix(transformation,pos,rot,scale);
- instance_entry["position"] = ll_sd_from_vector3(pos);
- instance_entry["rotation"] = ll_sd_from_quaternion(rot);
- instance_entry["scale"] = ll_sd_from_vector3(scale);
-
- instance_entry["material"] = LL_MCODE_WOOD;
- instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_NONE);
- instance_entry["mesh"] = mesh_index[data.mBaseModel];
-
- instance_entry["face_list"] = LLSD::emptyArray();
-
- // We want to be able to allow more than 8 materials...
- //
- S32 end = llmin((S32)instance.mMaterial.size(), instance.mModel->getNumVolumeFaces()) ;
-
- for (S32 face_num = 0; face_num < end; face_num++)
- {
- LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]];
- LLSD face_entry = LLSD::emptyMap();
-
- LLViewerFetchedTexture *texture = NULL;
-
- if (material.mDiffuseMapFilename.size())
- {
- texture = FindViewerTexture(material);
- }
-
- if ((texture != NULL) &&
- (textures.find(texture) == textures.end()))
- {
- textures.insert(texture);
- }
-
- std::stringstream texture_str;
- if (texture != NULL && include_textures && mUploadTextures)
- {
- if (texture->hasSavedRawImage())
- {
- LLImageDataLock lock(texture->getSavedRawImage());
-
- LLPointer<LLImageJ2C> upload_file =
- LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage());
-
- if (!upload_file.isNull() && upload_file->getDataSize())
- {
- texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize());
- }
- }
- }
-
- if (texture != NULL &&
- mUploadTextures &&
- texture_index.find(texture) == texture_index.end())
- {
- texture_index[texture] = texture_num;
- std::string str = texture_str.str();
- res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end());
- texture_num++;
- }
-
- // Subset of TextureEntry fields.
- if (texture != NULL && mUploadTextures)
- {
- face_entry["image"] = texture_index[texture];
- face_entry["scales"] = 1.0;
- face_entry["scalet"] = 1.0;
- face_entry["offsets"] = 0.0;
- face_entry["offsett"] = 0.0;
- face_entry["imagerot"] = 0.0;
- }
- face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor);
- face_entry["fullbright"] = material.mFullbright;
- instance_entry["face_list"][face_num] = face_entry;
- }
-
- res["instance_list"][instance_num] = instance_entry;
- instance_num++;
+ // These were handled above already...
+ continue;
}
+ packModelIntance(
+ iter.first,
+ iter.second,
+ model_name,
+ res,
+ mesh_num,
+ texture_num,
+ instance_num,
+ textures,
+ texture_index,
+ mesh_index,
+ texture_list_dest,
+ include_textures);
}
if (model_name.empty()) model_name = "mesh model";
@@ -2638,7 +3012,7 @@ void LLMeshUploadThread::generateHulls()
{
bool has_valid_requests = false ;
- for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ for (instance_map_t::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
{
LLMeshUploadData data;
data.mBaseModel = iter->first;
@@ -2710,7 +3084,8 @@ void LLMeshUploadThread::doWholeModelUpload()
LL_DEBUGS(LOG_MESH) << "Hull generation completed." << LL_ENDL;
mModelData = LLSD::emptyMap();
- wholeModelToLLSD(mModelData, true);
+ mTextureFiles.clear();
+ wholeModelToLLSD(mModelData, mTextureFiles, true);
LLSD body = mModelData["asset_resources"];
dump_llsd_to_file(body, make_dump_name("whole_model_body_", dump_num));
@@ -2763,7 +3138,8 @@ void LLMeshUploadThread::requestWholeModelFee()
generateHulls();
mModelData = LLSD::emptyMap();
- wholeModelToLLSD(mModelData, false);
+ mTextureFiles.clear();
+ wholeModelToLLSD(mModelData, mTextureFiles, false);
dump_llsd_to_file(mModelData, make_dump_name("whole_model_fee_request_", dump_num));
LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest,
mHttpPolicyClass,
@@ -2829,7 +3205,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp
body["error"] = LLSD::emptyMap();
body["error"]["message"] = reason;
body["error"]["identifier"] = "NetworkError"; // from asset-upload/upload_util.py
- log_upload_error(status, body, "upload", mModelData["name"].asString());
+ log_upload_error(status, body, "upload", mModelData["name"].asString(), mTextureFiles);
if (observer)
{
@@ -2864,7 +3240,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp
else
{
LL_WARNS(LOG_MESH) << "Upload failed. Not in expected 'complete' state." << LL_ENDL;
- log_upload_error(status, body, "upload", mModelData["name"].asString());
+ log_upload_error(status, body, "upload", mModelData["name"].asString(), mTextureFiles);
if (observer)
{
@@ -2889,7 +3265,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp
body["error"] = LLSD::emptyMap();
body["error"]["message"] = reason;
body["error"]["identifier"] = "NetworkError"; // from asset-upload/upload_util.py
- log_upload_error(status, body, "fee", mModelData["name"].asString());
+ log_upload_error(status, body, "fee", mModelData["name"].asString(), mTextureFiles);
if (observer)
{
@@ -2922,7 +3298,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp
else
{
LL_WARNS(LOG_MESH) << "Fee request failed. Not in expected 'upload' state." << LL_ENDL;
- log_upload_error(status, body, "fee", mModelData["name"].asString());
+ log_upload_error(status, body, "fee", mModelData["name"].asString(), mTextureFiles);
if (observer)
{
@@ -2943,15 +3319,17 @@ void LLMeshRepoThread::notifyLoadedMeshes()
return;
}
+ LL_PROFILE_ZONE_SCOPED;
+
if (!mLoadedQ.empty())
{
std::deque<LoadedMesh> loaded_queue;
- mMutex->lock();
+ mLoadedMutex->lock();
if (!mLoadedQ.empty())
{
loaded_queue.swap(mLoadedQ);
- mMutex->unlock();
+ mLoadedMutex->unlock();
update_metrics = true;
@@ -2960,44 +3338,52 @@ void LLMeshRepoThread::notifyLoadedMeshes()
{
if (mesh.mVolume->getNumVolumeFaces() > 0)
{
- gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume);
+ gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume, mesh.mLOD);
}
else
{
- gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams,
- LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
+ gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, mesh.mLOD, LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
}
}
}
+ else
+ {
+ mLoadedMutex->unlock();
+ }
}
if (!mUnavailableQ.empty())
{
std::deque<LODRequest> unavil_queue;
- mMutex->lock();
+ mLoadedMutex->lock();
if (!mUnavailableQ.empty())
{
unavil_queue.swap(mUnavailableQ);
- mMutex->unlock();
+ mLoadedMutex->unlock();
update_metrics = true;
// Process the elements free of the lock
for (const auto& req : unavil_queue)
{
- gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
+ gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD, req.mLOD);
}
}
+ else
+ {
+ mLoadedMutex->unlock();
+ }
}
- if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty())
+ if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || !mDecompositionQ.empty() || !mPhysicsQ.empty())
{
- if (mMutex->trylock())
+ if (mLoadedMutex->trylock())
{
std::deque<LLPointer<LLMeshSkinInfo>> skin_info_q;
std::deque<UUIDBasedRequest> skin_info_unavail_q;
std::list<LLModel::Decomposition*> decomp_q;
+ std::list<LLModel::Decomposition*> physics_q;
if (! mSkinInfoQ.empty())
{
@@ -3014,7 +3400,12 @@ void LLMeshRepoThread::notifyLoadedMeshes()
decomp_q.swap(mDecompositionQ);
}
- mMutex->unlock();
+ if (!mPhysicsQ.empty())
+ {
+ physics_q.swap(mPhysicsQ);
+ }
+
+ mLoadedMutex->unlock();
// Process the elements free of the lock
while (! skin_info_q.empty())
@@ -3030,9 +3421,15 @@ void LLMeshRepoThread::notifyLoadedMeshes()
while (! decomp_q.empty())
{
- gMeshRepo.notifyDecompositionReceived(decomp_q.front());
+ gMeshRepo.notifyDecompositionReceived(decomp_q.front(), false);
decomp_q.pop_front();
}
+
+ while (!physics_q.empty())
+ {
+ gMeshRepo.notifyDecompositionReceived(physics_q.front(), true);
+ physics_q.pop_front();
+ }
}
}
@@ -3051,9 +3448,11 @@ S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo
if (iter != mMeshHeader.end())
{
- auto& header = iter->second.second;
-
- return LLMeshRepository::getActualMeshLOD(header, lod);
+ auto& header = iter->second;
+ if (header.mHeaderSize > 0)
+ {
+ return LLMeshRepository::getActualMeshLOD(header, lod);
+ }
}
return lod;
@@ -3220,7 +3619,10 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
processData(body, body_offset, data, static_cast<S32>(data_size) - body_offset);
- delete [] data;
+ if (mHasDataOwnership)
+ {
+ delete [] data;
+ }
}
// Release handler
@@ -3253,7 +3655,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
<< LL_ENDL;
// Can't get the header so none of the LODs will be available
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)
{
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i));
@@ -3263,6 +3665,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
U8 * data, S32 data_size)
{
+ LL_PROFILE_ZONE_SCOPED;
LLUUID mesh_id = mMeshParams.getSculptID();
bool success = (!MESH_HEADER_PROCESS_FAILED)
&& ((data != NULL) == (data_size > 0)); // if we have data but no size or have size but no data, something is wrong;
@@ -3282,7 +3685,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
<< LL_ENDL;
// Can't get the header so none of the LODs will be available
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)
{
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i));
@@ -3298,8 +3701,8 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
LLMeshRepoThread::mesh_header_map::iterator iter = gMeshRepo.mThread->mMeshHeader.find(mesh_id);
if (iter != gMeshRepo.mThread->mMeshHeader.end())
{
- header_bytes = (S32)iter->second.first;
- header = iter->second.second;
+ header = iter->second;
+ header_bytes = header.mHeaderSize;
}
if (header_bytes > 0
@@ -3324,7 +3727,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
// LLSD is smart and can work like smart pointer, is not thread safe.
gMeshRepo.mThread->mHeaderMutex->unlock();
- S32 bytes = lod_bytes + header_bytes;
+ S32 bytes = lod_bytes + header_bytes + CACHE_PREAMBLE_SIZE;
// It's possible for the remote asset to have more data than is needed for the local cache
@@ -3337,6 +3740,11 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
LLMeshRepository::sCacheBytesWritten += data_size;
++LLMeshRepository::sCacheWrites;
+ // write preamble
+ U32 flags = header.getFlags();
+ write_preamble(file, header_bytes, flags);
+
+ // write header
file.write(data, data_size);
S32 remaining = bytes - file.tell();
@@ -3359,7 +3767,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
gMeshRepo.mThread->mHeaderMutex->unlock();
// headerReceived() parsed header, but header's data is invalid so none of the LODs will be available
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)
{
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i));
@@ -3388,9 +3796,64 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)
<< " (" << status.toTerseString() << "). Not retrying."
<< LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
}
+void LLMeshLODHandler::processLod(U8* data, S32 data_size)
+{
+ EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size);
+ if (result == MESH_OK)
+ {
+ // good fetch from sim, write to cache
+ LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
+
+ S32 offset = mOffset + CACHE_PREAMBLE_SIZE;
+ S32 size = mRequestedBytes;
+
+ if (file.getSize() >= offset + size)
+ {
+ S32 header_bytes = 0;
+ U32 flags = 0;
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID());
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // update header
+ if (!header.mLodInCache[mLOD])
+ {
+ header.mLodInCache[mLOD] = true;
+ header_bytes = header.mHeaderSize;
+ flags = header.getFlags();
+ }
+ // todo: handle else because we shouldn't have requested twice?
+ }
+ }
+ if (flags > 0)
+ {
+ write_preamble(file, header_bytes, flags);
+ }
+
+ file.seek(offset, 0);
+ file.write(data, size);
+ LLMeshRepository::sCacheBytesWritten += size;
+ ++LLMeshRepository::sCacheWrites;
+ }
+ }
+ else
+ {
+ LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID()
+ << ", Reason: " << result
+ << " LOD: " << mLOD
+ << " Data size: " << data_size
+ << " Not retrying."
+ << LL_ENDL;
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
+ gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
+ }
+}
void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
U8 * data, S32 data_size)
@@ -3399,33 +3862,31 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body
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
{
- EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size);
- if (result == MESH_OK)
+ LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this();
+ bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post(
+ [shrd_handler, data, data_size]
+ ()
{
- // good fetch from sim, write to cache
- LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
-
- S32 offset = mOffset;
- S32 size = mRequestedBytes;
-
- if (file.getSize() >= offset+size)
+ if (gMeshRepo.mThread->isShuttingDown())
{
- file.seek(offset);
- file.write(data, size);
- LLMeshRepository::sCacheBytesWritten += size;
- ++LLMeshRepository::sCacheWrites;
+ delete[] data;
+ return;
}
+ LLMeshLODHandler* handler = (LLMeshLODHandler * )shrd_handler.get();
+ handler->processLod(data, data_size);
+ delete[] data;
+ });
+
+ if (posted)
+ {
+ // ownership of data was passed to the lambda
+ mHasDataOwnership = false;
}
else
{
- LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID()
- << ", Reason: " << result
- << " LOD: " << mLOD
- << " Data size: " << data_size
- << " Not retrying."
- << LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
- gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
+ // mesh thread dies later than event queue, so this is normal
+ LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL;
+ processLod(data, data_size);
}
}
else
@@ -3435,7 +3896,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body
<< " LOD: " << mLOD
<< " Data size: " << data_size
<< LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
}
}
@@ -3446,6 +3907,7 @@ LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler()
{
LL_WARNS(LOG_MESH) << "deleting unprocessed request handler (may be ok on exit)" << LL_ENDL;
}
+ LLMeshRepoThread::decActiveSkinRequests();
}
void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
@@ -3454,38 +3916,103 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
<< ", Reason: " << status.toString()
<< " (" << status.toTerseString() << "). Not retrying."
<< LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);
}
-void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
- U8 * data, S32 data_size)
+void LLMeshSkinInfoHandler::processSkin(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))
+ if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
{
// good fetch from sim, write to cache
LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
- S32 offset = mOffset;
+ S32 offset = mOffset + CACHE_PREAMBLE_SIZE;
S32 size = mRequestedBytes;
- if (file.getSize() >= offset+size)
+ if (file.getSize() >= offset + size)
{
LLMeshRepository::sCacheBytesWritten += size;
++LLMeshRepository::sCacheWrites;
- file.seek(offset);
+
+ S32 header_bytes = 0;
+ U32 flags = 0;
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // update header
+ if (!header.mSkinInCache)
+ {
+ header.mSkinInCache = true;
+ header_bytes = header.mHeaderSize;
+ flags = header.getFlags();
+ }
+ // todo: handle else because we shouldn't have requested twice?
+ }
+ }
+ if (flags > 0)
+ {
+ write_preamble(file, header_bytes, flags);
+ }
+
+ file.seek(offset, 0);
file.write(data, size);
}
}
else
{
LL_WARNS(LOG_MESH) << "Error during mesh skin info processing. ID: " << mMeshID
+ << ", Unknown reason. Not retrying."
+ << LL_ENDL;
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
+ gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);
+ }
+}
+
+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
+ {
+ LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this();
+ bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post(
+ [shrd_handler, data, data_size]
+ ()
+ {
+ if (gMeshRepo.mThread->isShuttingDown())
+ {
+ delete[] data;
+ return;
+ }
+ LLMeshSkinInfoHandler* handler = (LLMeshSkinInfoHandler*)shrd_handler.get();
+ handler->processSkin(data, data_size);
+ delete[] data;
+ });
+
+ if (posted)
+ {
+ // ownership of data was passed to the lambda
+ mHasDataOwnership = false;
+ }
+ else
+ {
+ // mesh thread dies later than event queue, so this is normal
+ LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL;
+ processSkin(data, data_size);
+ }
+ }
+ else
+ {
+ LL_WARNS(LOG_MESH) << "Error during mesh skin info processing. ID: " << mMeshID
<< ", Unknown reason. Not retrying."
<< LL_ENDL;
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);
gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);
}
}
@@ -3519,14 +4046,39 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S
// good fetch from sim, write to cache
LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
- S32 offset = mOffset;
+ S32 offset = mOffset + CACHE_PREAMBLE_SIZE;
S32 size = mRequestedBytes;
if (file.getSize() >= offset+size)
{
LLMeshRepository::sCacheBytesWritten += size;
++LLMeshRepository::sCacheWrites;
- file.seek(offset);
+
+ S32 header_bytes = 0;
+ U32 flags = 0;
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // update header
+ if (!header.mPhysicsConvexInCache)
+ {
+ header.mPhysicsConvexInCache = true;
+ header_bytes = header.mHeaderSize;
+ flags = header.getFlags();
+ }
+ // todo: handle else because we shouldn't have requested twice?
+ }
+ }
+ if (flags > 0)
+ {
+ write_preamble(file, header_bytes, flags);
+ }
+
+ file.seek(offset, 0);
file.write(data, size);
}
}
@@ -3567,14 +4119,39 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3
// good fetch from sim, write to cache for caching
LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE);
- S32 offset = mOffset;
+ S32 offset = mOffset + CACHE_PREAMBLE_SIZE;
S32 size = mRequestedBytes;
if (file.getSize() >= offset+size)
{
LLMeshRepository::sCacheBytesWritten += size;
++LLMeshRepository::sCacheWrites;
- file.seek(offset);
+
+ S32 header_bytes = 0;
+ U32 flags = 0;
+ {
+ LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex);
+
+ LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID);
+ if (header_it != gMeshRepo.mThread->mMeshHeader.end())
+ {
+ LLMeshHeader& header = header_it->second;
+ // update header
+ if (!header.mPhysicsMeshInCache)
+ {
+ header.mPhysicsMeshInCache = true;
+ header_bytes = header.mHeaderSize;
+ flags = header.getFlags();
+ }
+ // todo: handle else because we shouldn't have requested twice?
+ }
+ }
+ if (flags > 0)
+ {
+ write_preamble(file, header_bytes, flags);
+ }
+
+ file.seek(offset, 0);
file.write(data, size);
}
}
@@ -3635,7 +4212,7 @@ void LLMeshRepository::shutdown()
mUploads[i]->discard() ; //discard the uploading requests.
}
- mThread->mSignal->broadcast();
+ mThread->cleanup();
while (!mThread->isStopped())
{
@@ -3700,45 +4277,46 @@ void LLMeshRepository::unregisterMesh(LLVOVolume* vobj)
{
for (auto& param : lod)
{
- vector_replace_with_last(param.second, vobj);
+ vector_replace_with_last(param.second.mVolumes, vobj);
}
}
for (auto& skin_pair : mLoadingSkins)
{
- vector_replace_with_last(skin_pair.second, vobj);
+ vector_replace_with_last(skin_pair.second.mVolumes, vobj);
}
}
-S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
+S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 new_lod, S32 last_lod)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
// Manage time-to-load metrics for mesh download operations.
metricsProgress(1);
- if (detail < 0 || detail >= LLVolumeLODGroup::NUM_LODS)
+ if (new_lod < 0 || new_lod >= LLVolumeLODGroup::NUM_LODS)
{
- return detail;
+ return new_lod;
}
{
LLMutexLock lock(mMeshMutex);
//add volume to list of loading meshes
const auto& mesh_id = mesh_params.getSculptID();
- mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_id);
- if (iter != mLoadingMeshes[detail].end())
+ mesh_load_map::iterator iter = mLoadingMeshes[new_lod].find(mesh_id);
+ if (iter != mLoadingMeshes[new_lod].end())
{ //request pending for this mesh, append volume id to list
- auto it = std::find(iter->second.begin(), iter->second.end(), vobj);
- if (it == iter->second.end()) {
- iter->second.push_back(vobj);
+ auto it = std::find(iter->second.mVolumes.begin(), iter->second.mVolumes.end(), vobj);
+ if (it == iter->second.mVolumes.end()) {
+ iter->second.addVolume(vobj);
}
}
else
{
//first request for this mesh
- mLoadingMeshes[detail][mesh_id].push_back(vobj);
- mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
+ std::shared_ptr<PendingRequestBase> request = std::make_shared<PendingRequestLOD>(mesh_params, new_lod);
+ mPendingRequests.emplace_back(request);
+ mLoadingMeshes[new_lod][mesh_id].initData(vobj, request);
LLMeshRepository::sLODPending++;
}
}
@@ -3767,7 +4345,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
}
//next, see what the next lowest LOD available might be
- for (S32 i = detail-1; i >= 0; --i)
+ for (S32 i = new_lod -1; i >= 0; --i)
{
LLVolume* lod = group->refLOD(i);
if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0)
@@ -3780,7 +4358,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
}
//no lower LOD is a available, is a higher lod available?
- for (S32 i = detail+1; i < LLVolumeLODGroup::NUM_LODS; ++i)
+ for (S32 i = new_lod+1; i < LLVolumeLODGroup::NUM_LODS; ++i)
{
LLVolume* lod = group->refLOD(i);
if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0)
@@ -3794,7 +4372,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
}
}
- return detail;
+ return new_lod;
}
void LLMeshRepository::notifyLoadedMeshes()
@@ -3918,6 +4496,7 @@ void LLMeshRepository::notifyLoadedMeshes()
// erase from background thread
mThread->mWorkQueue.post([=, this]()
{
+ LLMutexLock(mThread->mSkinMapMutex);
mThread->mSkinMap.erase(id);
});
}
@@ -3932,13 +4511,20 @@ void LLMeshRepository::notifyLoadedMeshes()
{
LLMutexTrylock lock1(mMeshMutex);
LLMutexTrylock lock2(mThread->mMutex);
+ LLMutexTrylock lock3(mThread->mHeaderMutex);
+ LLMutexTrylock lock4(mThread->mPendingMutex);
static U32 hold_offs(0);
- if (! lock1.isLocked() || ! lock2.isLocked())
+ if (! lock1.isLocked() || ! lock2.isLocked() || ! lock3.isLocked() || ! lock4.isLocked())
{
// If we can't get the locks, skip and pick this up later.
+ // Eventually thread queue will be free enough
++hold_offs;
sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs);
+ if (hold_offs > 4)
+ {
+ LL_WARNS_ONCE() << "High mesh thread holdoff" << LL_ENDL;
+ }
return;
}
hold_offs = 0;
@@ -3974,72 +4560,61 @@ void LLMeshRepository::notifyLoadedMeshes()
mUploadErrorQ.pop();
}
- S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests;
- if (active_count < LLMeshRepoThread::sRequestLowWater)
+ // mPendingRequests go into queues, queues go into active http requests.
+ // Checking sRequestHighWater to keep queues at least somewhat populated
+ // for faster transition into http
+ S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests + LLMeshRepoThread::sActiveSkinRequests;
+ active_count += (S32)(mThread->mLODReqQ.size() + mThread->mHeaderReqQ.size() + mThread->mSkinInfoQ.size());
+ if (active_count < LLMeshRepoThread::sRequestHighWater)
{
S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count;
if (mPendingRequests.size() > push_count)
{
+ LL_PROFILE_ZONE_NAMED("Mesh score update");
// More requests than the high-water limit allows so
// sort and forward the most important.
- //calculate "score" for pending requests
-
- //create score map
- std::map<LLUUID, F32> score_map;
-
- for (U32 i = 0; i < LLVolumeLODGroup::NUM_LODS; ++i)
+ // update "score" for pending requests
+ for (std::shared_ptr<PendingRequestBase>& req_p : mPendingRequests)
{
- for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter)
- {
- F32 max_score = 0.f;
- for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
- {
- LLVOVolume* object = *obj_iter;
- if (object)
- {
- LLDrawable* drawable = object->mDrawable;
- if (drawable)
- {
- F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f);
- max_score = llmax(max_score, cur_score);
- }
- }
- }
-
- score_map[iter->first] = max_score;
- }
- }
-
- //set "score" for pending requests
- for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter)
- {
- iter->mScore = score_map[iter->mMeshParams.getSculptID()];
+ req_p->checkScore();
}
//sort by "score"
std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count,
- mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater());
+ mPendingRequests.end(), PendingRequestBase::CompareScoreGreater());
}
-
while (!mPendingRequests.empty() && push_count > 0)
{
- LLMeshRepoThread::LODRequest& request = mPendingRequests.front();
- mThread->loadMeshLOD(request.mMeshParams, request.mLOD);
+ std::shared_ptr<PendingRequestBase>& req_p = mPendingRequests.front();
+ // todo: check hasTrackedData here and erase request if none
+ // since this is supposed to mean that request was removed
+ switch (req_p->getRequestType())
+ {
+ case MESH_REQUEST_LOD:
+ {
+ PendingRequestLOD* lod = (PendingRequestLOD*)req_p.get();
+ mThread->loadMeshLOD(lod->mMeshParams, lod->mLOD);
+ LLMeshRepository::sLODPending--;
+ break;
+ }
+ case MESH_REQUEST_SKIN:
+ {
+ PendingRequestUUID* skin = (PendingRequestUUID*)req_p.get();
+ mThread->loadMeshSkinInfo(skin->getId());
+ break;
+ }
+
+ default:
+ LL_ERRS() << "Unknown request type in LLMeshRepository::notifyLoadedMeshes" << LL_ENDL;
+ break;
+ }
mPendingRequests.erase(mPendingRequests.begin());
- LLMeshRepository::sLODPending--;
push_count--;
}
}
- //send skin info requests
- while (!mPendingSkinRequests.empty())
- {
- mThread->loadMeshSkinInfo(mPendingSkinRequests.front());
- mPendingSkinRequests.pop();
- }
-
//send decomposition requests
while (!mPendingDecompositionRequests.empty())
{
@@ -4069,7 +4644,7 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo* info)
skin_load_map::iterator iter = mLoadingSkins.find(info->mMeshID);
if (iter != mLoadingSkins.end())
{
- for (LLVOVolume* vobj : iter->second)
+ for (LLVOVolume* vobj : iter->second.mVolumes)
{
if (vobj)
{
@@ -4085,7 +4660,7 @@ void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id)
skin_load_map::iterator iter = mLoadingSkins.find(mesh_id);
if (iter != mLoadingSkins.end())
{
- for (LLVOVolume* vobj : iter->second)
+ for (LLVOVolume* vobj : iter->second.mVolumes)
{
if (vobj)
{
@@ -4096,13 +4671,13 @@ void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id)
}
}
-void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp)
+void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp, bool physics_mesh)
{
- decomposition_map::iterator iter = mDecompositionMap.find(decomp->mMeshID);
+ LLUUID decomp_id = decomp->mMeshID; // Copy to avoid invalidation in below deletion
+ decomposition_map::iterator iter = mDecompositionMap.find(decomp_id);
if (iter == mDecompositionMap.end())
{ //just insert decomp into map
- mDecompositionMap[decomp->mMeshID] = decomp;
- mLoadingDecompositions.erase(decomp->mMeshID);
+ mDecompositionMap[decomp_id] = decomp;
sCacheBytesDecomps += decomp->sizeBytes();
}
else
@@ -4110,21 +4685,27 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom
sCacheBytesDecomps -= iter->second->sizeBytes();
iter->second->merge(decomp);
sCacheBytesDecomps += iter->second->sizeBytes();
-
- mLoadingDecompositions.erase(decomp->mMeshID);
delete decomp;
}
+
+ if (physics_mesh)
+ {
+ mLoadingPhysicsShapes.erase(decomp_id);
+ }
+ else
+ {
+ mLoadingDecompositions.erase(decomp_id);
+ }
}
-void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
+void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod)
{ //called from main thread
- S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
//get list of objects waiting to be notified this mesh is loaded
const auto& mesh_id = mesh_params.getSculptID();
- mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_id);
+ mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id);
- if (volume && obj_iter != mLoadingMeshes[detail].end())
+ if (volume && obj_iter != mLoadingMeshes[lod].end())
{
//make sure target volume is still valid
if (volume->getNumVolumeFaces() <= 0)
@@ -4134,6 +4715,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
}
{ //update system volume
+ S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail);
if (sys_volume)
{
@@ -4149,7 +4731,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
}
//notify waiting LLVOVolume instances that their requested mesh is available
- for (LLVOVolume* vobj : obj_iter->second)
+ for (LLVOVolume* vobj : obj_iter->second.mVolumes)
{
if (vobj)
{
@@ -4157,29 +4739,29 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
}
}
- mLoadingMeshes[detail].erase(obj_iter);
+ mLoadingMeshes[lod].erase(obj_iter);
LLViewerStatsRecorder::instance().meshLoaded();
}
}
-void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)
+void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod)
{ //called from main thread
//get list of objects waiting to be notified this mesh is loaded
const auto& mesh_id = mesh_params.getSculptID();
- mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id);
- if (obj_iter != mLoadingMeshes[lod].end())
+ mesh_load_map::iterator obj_iter = mLoadingMeshes[request_lod].find(mesh_id);
+ if (obj_iter != mLoadingMeshes[request_lod].end())
{
- F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
+ F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(volume_lod);
- LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, lod);
+ LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, volume_lod);
if (sys_volume)
{
sys_volume->setMeshAssetUnavaliable(true);
LLPrimitive::getVolumeManager()->unrefVolume(sys_volume);
}
- for (LLVOVolume* vobj : obj_iter->second)
+ for (LLVOVolume* vobj : obj_iter->second.mVolumes)
{
if (vobj)
{
@@ -4189,12 +4771,12 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,
obj_volume->getDetail() == detail &&
obj_volume->getParams() == mesh_params)
{ //should force volume to find most appropriate LOD
- vobj->setVolume(obj_volume->getParams(), lod);
+ vobj->setVolume(obj_volume->getParams(), volume_lod);
}
}
}
- mLoadingMeshes[lod].erase(obj_iter);
+ mLoadingMeshes[request_lod].erase(obj_iter);
}
}
@@ -4222,16 +4804,17 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV
skin_load_map::iterator iter = mLoadingSkins.find(mesh_id);
if (iter != mLoadingSkins.end())
{ //request pending for this mesh, append volume id to list
- auto it = std::find(iter->second.begin(), iter->second.end(), requesting_obj);
- if (it == iter->second.end()) {
- iter->second.push_back(requesting_obj);
+ auto it = std::find(iter->second.mVolumes.begin(), iter->second.mVolumes.end(), requesting_obj);
+ if (it == iter->second.mVolumes.end()) {
+ iter->second.addVolume(requesting_obj);
}
}
else
{
//first request for this mesh
- mLoadingSkins[mesh_id].push_back(requesting_obj);
- mPendingSkinRequests.push(mesh_id);
+ std::shared_ptr<PendingRequestBase> request = std::make_shared<PendingRequestUUID>(mesh_id, MESH_REQUEST_SKIN);
+ mLoadingSkins[mesh_id].initData(requesting_obj, request);
+ mPendingRequests.emplace_back(request);
}
}
}
@@ -4259,7 +4842,6 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& 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
mLoadingPhysicsShapes.insert(mesh_id);
mPendingPhysicsShapeRequests.push(mesh_id);
}
@@ -4358,7 +4940,7 @@ bool LLMeshRepository::hasSkinInfo(const LLUUID& mesh_id)
return false;
}
-bool LLMeshRepository::hasHeader(const LLUUID& mesh_id)
+bool LLMeshRepository::hasHeader(const LLUUID& mesh_id) const
{
if (mesh_id.isNull())
{
@@ -4368,13 +4950,13 @@ bool LLMeshRepository::hasHeader(const LLUUID& mesh_id)
return mThread->hasHeader(mesh_id);
}
-bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id)
+bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) const
{
LLMutexLock lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
- if (iter != mMeshHeader.end() && iter->second.first > 0)
+ mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id);
+ if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- LLMeshHeader &mesh = iter->second.second;
+ const LLMeshHeader &mesh = iter->second;
if (mesh.mPhysicsMeshSize > 0)
{
return true;
@@ -4384,13 +4966,13 @@ bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id)
return false;
}
-bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id)
+bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) const
{
LLMutexLock lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
- if (iter != mMeshHeader.end() && iter->second.first > 0)
+ mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id);
+ if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- LLMeshHeader& mesh = iter->second.second;
+ const LLMeshHeader& mesh = iter->second;
if (mesh.mSkinOffset >= 0
&& mesh.mSkinSize > 0)
{
@@ -4401,34 +4983,35 @@ bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id)
return false;
}
-bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id)
+bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) const
{
LLMutexLock lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
+ mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id);
return iter != mMeshHeader.end();
}
-void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures,
+void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, const std::map<std::string, std::string> &lod_sources,
+ LLVector3& scale, bool upload_textures,
bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position,
- std::string upload_url, bool do_upload,
+ std::string upload_url, const LLUUID& destination_folder_id, bool do_upload,
LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer)
{
- LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures,
+ LLMeshUploadThread* thread = new LLMeshUploadThread(data, lod_sources, scale, upload_textures,
upload_skin, upload_joints, lock_scale_if_joint_position,
- upload_url, do_upload, fee_observer, upload_observer);
+ upload_url, destination_folder_id, do_upload, fee_observer, upload_observer);
mUploadWaitList.push_back(thread);
}
-S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod)
+S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
if (mThread && mesh_id.notNull() && LLPrimitive::NO_LOD != lod)
{
LLMutexLock lock(mThread->mHeaderMutex);
- LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
- if (iter != mThread->mMeshHeader.end() && iter->second.first > 0)
+ LLMeshRepoThread::mesh_header_map::const_iterator iter = mThread->mMeshHeader.find(mesh_id);
+ if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- const LLMeshHeader& header = iter->second.second;
+ const LLMeshHeader& header = iter->second;
if (header.m404)
{
@@ -4532,9 +5115,9 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* by
{
LLMutexLock lock(mThread->mHeaderMutex);
LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
- if (iter != mThread->mMeshHeader.end() && iter->second.first > 0)
+ if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- result = getStreamingCostLegacy(iter->second.second, radius, bytes, bytes_visible, lod, unscaled_value);
+ result = getStreamingCostLegacy(iter->second, radius, bytes, bytes_visible, lod, unscaled_value);
}
}
if (result > 0.f)
@@ -4726,7 +5309,7 @@ bool LLMeshCostData::init(const LLMeshHeader& header)
}
-S32 LLMeshCostData::getSizeByLOD(S32 lod)
+S32 LLMeshCostData::getSizeByLOD(S32 lod) const
{
if (llclamp(lod,0,3) != lod)
{
@@ -4735,12 +5318,12 @@ S32 LLMeshCostData::getSizeByLOD(S32 lod)
return mSizeByLOD[lod];
}
-S32 LLMeshCostData::getSizeTotal()
+S32 LLMeshCostData::getSizeTotal() const
{
return mSizeByLOD[0] + mSizeByLOD[1] + mSizeByLOD[2] + mSizeByLOD[3];
}
-F32 LLMeshCostData::getEstTrisByLOD(S32 lod)
+F32 LLMeshCostData::getEstTrisByLOD(S32 lod) const
{
if (llclamp(lod,0,3) != lod)
{
@@ -4749,12 +5332,12 @@ F32 LLMeshCostData::getEstTrisByLOD(S32 lod)
return mEstTrisByLOD[lod];
}
-F32 LLMeshCostData::getEstTrisMax()
+F32 LLMeshCostData::getEstTrisMax() const
{
return llmax(mEstTrisByLOD[0], mEstTrisByLOD[1], mEstTrisByLOD[2], mEstTrisByLOD[3]);
}
-F32 LLMeshCostData::getRadiusWeightedTris(F32 radius)
+F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) const
{
F32 max_distance = 512.f;
@@ -4798,7 +5381,7 @@ F32 LLMeshCostData::getRadiusWeightedTris(F32 radius)
return weighted_avg;
}
-F32 LLMeshCostData::getEstTrisForStreamingCost()
+F32 LLMeshCostData::getEstTrisForStreamingCost() const
{
LL_DEBUGS("StreamingCost") << "tris_by_lod: "
<< mEstTrisByLOD[0] << ", "
@@ -4808,7 +5391,7 @@ F32 LLMeshCostData::getEstTrisForStreamingCost()
F32 charged_tris = mEstTrisByLOD[3];
F32 allowed_tris = mEstTrisByLOD[3];
- const F32 ENFORCE_FLOOR = 64.0f;
+ constexpr F32 ENFORCE_FLOOR = 64.0f;
for (S32 i=2; i>=0; i--)
{
// How many tris can we have in this LOD without affecting land impact?
@@ -4825,13 +5408,13 @@ F32 LLMeshCostData::getEstTrisForStreamingCost()
return charged_tris;
}
-F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius)
+F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) const
{
static LLCachedControl<U32> mesh_triangle_budget(gSavedSettings, "MeshTriangleBudget");
return getRadiusWeightedTris(radius)/mesh_triangle_budget*15000.f;
}
-F32 LLMeshCostData::getTriangleBasedStreamingCost()
+F32 LLMeshCostData::getTriangleBasedStreamingCost() const
{
F32 result = ANIMATED_OBJECT_COST_PER_KTRI * 0.001f * getEstTrisForStreamingCost();
return result;
@@ -4846,9 +5429,9 @@ bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data)
{
LLMutexLock lock(mThread->mHeaderMutex);
LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
- if (iter != mThread->mMeshHeader.end() && iter->second.first > 0)
+ if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)
{
- LLMeshHeader& header = iter->second.second;
+ LLMeshHeader& header = iter->second;
bool header_invalid = (header.m404
|| header.mLodSize[0] <= 0
@@ -5405,13 +5988,7 @@ bool LLMeshRepository::meshUploadEnabled()
bool LLMeshRepository::meshRezEnabled()
{
static LLCachedControl<bool> mesh_enabled(gSavedSettings, "MeshEnabled");
- LLViewerRegion *region = gAgent.getRegion();
- if(mesh_enabled &&
- region)
- {
- return region->meshRezEnabled();
- }
- return false;
+ return mesh_enabled;
}
// Threading: main thread only