diff options
author | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-04-29 07:43:28 +0300 |
---|---|---|
committer | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-04-29 07:56:09 +0300 |
commit | 1b68f71348ecf3983b76b40d7940da8377f049b7 (patch) | |
tree | 2974eddaef130a067c26033d60a59fc790365b3d /indra/newview/llmeshrepository.cpp | |
parent | af4ea94efc1999f3b19fd8d643d0331f0b77e265 (diff) |
#824 Process source files in bulk: replace tabs with spaces, convert CRLF to LF, and trim trailing whitespaces as needed
Diffstat (limited to 'indra/newview/llmeshrepository.cpp')
-rw-r--r-- | indra/newview/llmeshrepository.cpp | 7270 |
1 files changed, 3635 insertions, 3635 deletions
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index a5601dafbe..f6441f8404 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -5,21 +5,21 @@ * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010-2014, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -324,17 +324,17 @@ static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); // suitable number. In production, all must be false. // // Example: -// #define MESH_HTTP_RESPONSE_FAILED MESH_RANDOM_NTH_TRUE(9) +// #define MESH_HTTP_RESPONSE_FAILED MESH_RANDOM_NTH_TRUE(9) // 1-in-N calls will test true -#define MESH_RANDOM_NTH_TRUE(_N) ( ll_rand(S32(_N)) == 0 ) +#define MESH_RANDOM_NTH_TRUE(_N) ( ll_rand(S32(_N)) == 0 ) -#define MESH_HTTP_RESPONSE_FAILED false -#define MESH_HEADER_PROCESS_FAILED false -#define MESH_LOD_PROCESS_FAILED false -#define MESH_SKIN_INFO_PROCESS_FAILED false -#define MESH_DECOMP_PROCESS_FAILED false -#define MESH_PHYS_SHAPE_PROCESS_FAILED false +#define MESH_HTTP_RESPONSE_FAILED false +#define MESH_HEADER_PROCESS_FAILED false +#define MESH_LOD_PROCESS_FAILED false +#define MESH_SKIN_INFO_PROCESS_FAILED false +#define MESH_DECOMP_PROCESS_FAILED false +#define MESH_PHYS_SHAPE_PROCESS_FAILED false // -------------------------------------------------------------------------- @@ -344,14 +344,14 @@ LLMeshRepository gMeshRepo; const 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_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; -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 +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 const U32 DOWNLOAD_RETRY_LIMIT = 8; const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds @@ -366,10 +366,10 @@ const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds // upload retries to the user as in the past. SH-4667. const long UPLOAD_RETRY_LIMIT = 0L; -// Maximum mesh version to support. Three least significant digits are reserved for the minor version, +// 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 -// be parsed by viewers that don't specifically support that version. For example, if the integer "1" is -// present, the version is 0.001. A viewer that can parse version 0.001 can also parse versions up to 0.999, +// be parsed by viewers that don't specifically support that version. For example, if the integer "1" is +// 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; @@ -391,16 +391,16 @@ U32 LLMeshRepository::sCacheBytesDecomps = 0; U32 LLMeshRepository::sCacheReads = 0; U32 LLMeshRepository::sCacheWrites = 0; U32 LLMeshRepository::sMaxLockHoldoffs = 0; - -LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false); // true -> gather cpu metrics + +LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false); // true -> gather cpu metrics namespace { - // The NoOpDeletor is used when passing certain objects (generally the LLMeshUploadThread) - // in a smart pointer below for passage into the LLCore::Http libararies. - // When the smart pointer is destroyed, no action will be taken since we + // The NoOpDeletor is used when passing certain objects (generally the LLMeshUploadThread) + // in a smart pointer below for passage into the LLCore::Http libararies. + // When the smart pointer is destroyed, no action will be taken since we // do not in these cases want the object to be destroyed at the end of the call. - // - // *NOTE$: Yes! It is "Deletor" + // + // *NOTE$: Yes! It is "Deletor" // http://english.stackexchange.com/questions/4733/what-s-the-rule-for-adding-er-vs-or-when-nouning-a-verb // "delete" derives from Latin "deletus" @@ -411,17 +411,17 @@ namespace { static S32 dump_num = 0; std::string make_dump_name(std::string prefix, S32 num) { - return prefix + std::to_string(num) + std::string(".xml"); + return prefix + std::to_string(num) + std::string(".xml"); } void dump_llsd_to_file(const LLSD& content, std::string filename); LLSD llsd_from_file(std::string filename); -const std::string header_lod[] = +const std::string header_lod[] = { - "lowest_lod", - "low_lod", - "medium_lod", - "high_lod" + "lowest_lod", + "low_lod", + "medium_lod", + "high_lod" }; const char * const LOG_MESH = "Mesh"; @@ -445,81 +445,81 @@ void on_new_single_inventory_upload_complete( //get the number of bytes resident in memory for given volume U32 get_volume_memory_size(const LLVolume* volume) { - U32 indices = 0; - U32 vertices = 0; + U32 indices = 0; + U32 vertices = 0; - for (U32 i = 0; i < volume->getNumVolumeFaces(); ++i) - { - const LLVolumeFace& face = volume->getVolumeFace(i); - indices += face.mNumIndices; - vertices += face.mNumVertices; - } + for (U32 i = 0; i < volume->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = volume->getVolumeFace(i); + indices += face.mNumIndices; + vertices += face.mNumVertices; + } - return indices*2+vertices*11+sizeof(LLVolume)+sizeof(LLVolumeFace)*volume->getNumVolumeFaces(); + return indices*2+vertices*11+sizeof(LLVolume)+sizeof(LLVolumeFace)*volume->getNumVolumeFaces(); } void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, F32 scale = 1.f) { - res.mPositions.clear(); - res.mNormals.clear(); - - const F32* v = mesh.mVertexBase; - - if (mesh.mIndexType == LLCDMeshData::INT_16) - { - U16* idx = (U16*) mesh.mIndexBase; - for (S32 j = 0; j < mesh.mNumTriangles; ++j) - { - F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes); - F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes); - F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes); - - idx = (U16*) (((U8*)idx)+mesh.mIndexStrideBytes); - - LLVector3 v0(mp0); - LLVector3 v1(mp1); - LLVector3 v2(mp2); - - LLVector3 n = (v1-v0)%(v2-v0); - n.normalize(); - - res.mPositions.push_back(v0*scale); - res.mPositions.push_back(v1*scale); - res.mPositions.push_back(v2*scale); - - res.mNormals.push_back(n); - res.mNormals.push_back(n); - res.mNormals.push_back(n); - } - } - else - { - U32* idx = (U32*) mesh.mIndexBase; - for (S32 j = 0; j < mesh.mNumTriangles; ++j) - { - F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes); - F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes); - F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes); - - idx = (U32*) (((U8*)idx)+mesh.mIndexStrideBytes); - - LLVector3 v0(mp0); - LLVector3 v1(mp1); - LLVector3 v2(mp2); - - LLVector3 n = (v1-v0)%(v2-v0); - n.normalize(); - - res.mPositions.push_back(v0*scale); - res.mPositions.push_back(v1*scale); - res.mPositions.push_back(v2*scale); - - res.mNormals.push_back(n); - res.mNormals.push_back(n); - res.mNormals.push_back(n); - } - } + res.mPositions.clear(); + res.mNormals.clear(); + + const F32* v = mesh.mVertexBase; + + if (mesh.mIndexType == LLCDMeshData::INT_16) + { + U16* idx = (U16*) mesh.mIndexBase; + for (S32 j = 0; j < mesh.mNumTriangles; ++j) + { + F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes); + F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes); + F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes); + + idx = (U16*) (((U8*)idx)+mesh.mIndexStrideBytes); + + LLVector3 v0(mp0); + LLVector3 v1(mp1); + LLVector3 v2(mp2); + + LLVector3 n = (v1-v0)%(v2-v0); + n.normalize(); + + res.mPositions.push_back(v0*scale); + res.mPositions.push_back(v1*scale); + res.mPositions.push_back(v2*scale); + + res.mNormals.push_back(n); + res.mNormals.push_back(n); + res.mNormals.push_back(n); + } + } + else + { + U32* idx = (U32*) mesh.mIndexBase; + for (S32 j = 0; j < mesh.mNumTriangles; ++j) + { + F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes); + F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes); + F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes); + + idx = (U32*) (((U8*)idx)+mesh.mIndexStrideBytes); + + LLVector3 v0(mp0); + LLVector3 v1(mp1); + LLVector3 v2(mp2); + + LLVector3 n = (v1-v0)%(v2-v0); + n.normalize(); + + res.mPositions.push_back(v0*scale); + res.mPositions.push_back(v1*scale); + res.mPositions.push_back(v2*scale); + + res.mNormals.push_back(n); + res.mNormals.push_back(n); + res.mNormals.push_back(n); + } + } } void RequestStats::updateTime() @@ -542,13 +542,13 @@ bool RequestStats::isDelayed() const LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMaterial& material) { - LLPointer< LLViewerFetchedTexture > * ppTex = static_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData); - return ppTex ? (*ppTex).get() : NULL; + LLPointer< LLViewerFetchedTexture > * ppTex = static_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData); + return ppTex ? (*ppTex).get() : NULL; } volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; -U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; +U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN; S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; S32 LLMeshRepoThread::sRequestWaterLevel = 0; @@ -577,34 +577,34 @@ class LLMeshHandlerBase : public LLCore::HttpHandler, public: typedef std::shared_ptr<LLMeshHandlerBase> ptr_t; - LOG_CLASS(LLMeshHandlerBase); - LLMeshHandlerBase(U32 offset, U32 requested_bytes) - : LLCore::HttpHandler(), - mMeshParams(), - mProcessed(false), - mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), - mOffset(offset), - mRequestedBytes(requested_bytes) - {} + LOG_CLASS(LLMeshHandlerBase); + LLMeshHandlerBase(U32 offset, U32 requested_bytes) + : LLCore::HttpHandler(), + mMeshParams(), + mProcessed(false), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), + mOffset(offset), + mRequestedBytes(requested_bytes) + {} - virtual ~LLMeshHandlerBase() - {} + virtual ~LLMeshHandlerBase() + {} protected: - LLMeshHandlerBase(const LLMeshHandlerBase &); // Not defined - void operator=(const LLMeshHandlerBase &); // Not defined - + LLMeshHandlerBase(const LLMeshHandlerBase &); // Not defined + void operator=(const LLMeshHandlerBase &); // Not defined + public: - virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); - virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size) = 0; - virtual void processFailure(LLCore::HttpStatus status) = 0; - + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size) = 0; + virtual void processFailure(LLCore::HttpStatus status) = 0; + public: - LLVolumeParams mMeshParams; - bool mProcessed; - LLCore::HttpHandle mHttpHandle; - U32 mOffset; - U32 mRequestedBytes; + LLVolumeParams mMeshParams; + bool mProcessed; + LLCore::HttpHandle mHttpHandle; + U32 mOffset; + U32 mRequestedBytes; }; @@ -614,22 +614,22 @@ public: class LLMeshHeaderHandler : public LLMeshHandlerBase { public: - LOG_CLASS(LLMeshHeaderHandler); - LLMeshHeaderHandler(const LLVolumeParams & mesh_params, U32 offset, U32 requested_bytes) - : LLMeshHandlerBase(offset, requested_bytes) - { - mMeshParams = mesh_params; - LLMeshRepoThread::incActiveHeaderRequests(); - } - virtual ~LLMeshHeaderHandler(); + LOG_CLASS(LLMeshHeaderHandler); + LLMeshHeaderHandler(const LLVolumeParams & mesh_params, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(offset, requested_bytes) + { + mMeshParams = mesh_params; + LLMeshRepoThread::incActiveHeaderRequests(); + } + virtual ~LLMeshHeaderHandler(); protected: - LLMeshHeaderHandler(const LLMeshHeaderHandler &); // Not defined - void operator=(const LLMeshHeaderHandler &); // Not defined - + LLMeshHeaderHandler(const LLMeshHeaderHandler &); // Not defined + void operator=(const LLMeshHeaderHandler &); // Not defined + public: - virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); - virtual void processFailure(LLCore::HttpStatus status); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); }; @@ -639,26 +639,26 @@ public: class LLMeshLODHandler : public LLMeshHandlerBase { public: - LOG_CLASS(LLMeshLODHandler); - LLMeshLODHandler(const LLVolumeParams & mesh_params, S32 lod, U32 offset, U32 requested_bytes) - : LLMeshHandlerBase(offset, requested_bytes), - mLOD(lod) - { - mMeshParams = mesh_params; - LLMeshRepoThread::incActiveLODRequests(); - } - virtual ~LLMeshLODHandler(); - + LOG_CLASS(LLMeshLODHandler); + LLMeshLODHandler(const LLVolumeParams & mesh_params, S32 lod, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(offset, requested_bytes), + mLOD(lod) + { + mMeshParams = mesh_params; + LLMeshRepoThread::incActiveLODRequests(); + } + virtual ~LLMeshLODHandler(); + protected: - LLMeshLODHandler(const LLMeshLODHandler &); // Not defined - void operator=(const LLMeshLODHandler &); // Not defined - + LLMeshLODHandler(const LLMeshLODHandler &); // Not defined + void operator=(const LLMeshLODHandler &); // Not defined + public: - virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); - virtual void processFailure(LLCore::HttpStatus status); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); public: - S32 mLOD; + S32 mLOD; }; @@ -668,23 +668,23 @@ public: class LLMeshSkinInfoHandler : public LLMeshHandlerBase { public: - LOG_CLASS(LLMeshSkinInfoHandler); - LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 requested_bytes) - : LLMeshHandlerBase(offset, requested_bytes), - mMeshID(id) - {} - virtual ~LLMeshSkinInfoHandler(); + LOG_CLASS(LLMeshSkinInfoHandler); + LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(offset, requested_bytes), + mMeshID(id) + {} + virtual ~LLMeshSkinInfoHandler(); protected: - LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &); // Not defined - void operator=(const LLMeshSkinInfoHandler &); // Not defined + LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &); // Not defined + void operator=(const LLMeshSkinInfoHandler &); // Not defined public: - virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); - virtual void processFailure(LLCore::HttpStatus status); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); public: - LLUUID mMeshID; + LLUUID mMeshID; }; @@ -694,23 +694,23 @@ public: class LLMeshDecompositionHandler : public LLMeshHandlerBase { public: - LOG_CLASS(LLMeshDecompositionHandler); - LLMeshDecompositionHandler(const LLUUID& id, U32 offset, U32 requested_bytes) - : LLMeshHandlerBase(offset, requested_bytes), - mMeshID(id) - {} - virtual ~LLMeshDecompositionHandler(); + LOG_CLASS(LLMeshDecompositionHandler); + LLMeshDecompositionHandler(const LLUUID& id, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(offset, requested_bytes), + mMeshID(id) + {} + virtual ~LLMeshDecompositionHandler(); protected: - LLMeshDecompositionHandler(const LLMeshDecompositionHandler &); // Not defined - void operator=(const LLMeshDecompositionHandler &); // Not defined + LLMeshDecompositionHandler(const LLMeshDecompositionHandler &); // Not defined + void operator=(const LLMeshDecompositionHandler &); // Not defined public: - virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); - virtual void processFailure(LLCore::HttpStatus status); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); public: - LLUUID mMeshID; + LLUUID mMeshID; }; @@ -720,105 +720,105 @@ public: class LLMeshPhysicsShapeHandler : public LLMeshHandlerBase { public: - LOG_CLASS(LLMeshPhysicsShapeHandler); - LLMeshPhysicsShapeHandler(const LLUUID& id, U32 offset, U32 requested_bytes) - : LLMeshHandlerBase(offset, requested_bytes), - mMeshID(id) - {} - virtual ~LLMeshPhysicsShapeHandler(); + LOG_CLASS(LLMeshPhysicsShapeHandler); + LLMeshPhysicsShapeHandler(const LLUUID& id, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(offset, requested_bytes), + mMeshID(id) + {} + virtual ~LLMeshPhysicsShapeHandler(); protected: - LLMeshPhysicsShapeHandler(const LLMeshPhysicsShapeHandler &); // Not defined - void operator=(const LLMeshPhysicsShapeHandler &); // Not defined + LLMeshPhysicsShapeHandler(const LLMeshPhysicsShapeHandler &); // Not defined + void operator=(const LLMeshPhysicsShapeHandler &); // Not defined public: - virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); - virtual void processFailure(LLCore::HttpStatus status); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); public: - LLUUID mMeshID; + LLUUID mMeshID; }; void log_upload_error(LLCore::HttpStatus status, const LLSD& content, - const char * const stage, const std::string & model_name) -{ - // Add notification popup. - LLSD args; - std::string message = content["error"]["message"].asString(); - std::string identifier = content["error"]["identifier"].asString(); - args["MESSAGE"] = message; - args["IDENTIFIER"] = identifier; - args["LABEL"] = model_name; - - // Log details. - LL_WARNS(LOG_MESH) << "Error in stage: " << stage - << ", Reason: " << status.toString() - << " (" << status.toTerseString() << ")" << LL_ENDL; - - std::ostringstream details; - typedef std::set<std::string> mav_errors_set_t; - mav_errors_set_t mav_errors; - - if (content.has("error")) - { - const LLSD& err = content["error"]; - LL_WARNS(LOG_MESH) << "error: " << err << LL_ENDL; - LL_WARNS(LOG_MESH) << " mesh upload failed, stage '" << stage - << "', error '" << err["error"].asString() - << "', message '" << err["message"].asString() - << "', id '" << err["identifier"].asString() - << "'" << LL_ENDL; - - if (err.has("errors")) - { - details << std::endl << std::endl; - - S32 error_num = 0; - const LLSD& err_list = err["errors"]; - for (LLSD::array_const_iterator it = err_list.beginArray(); - it != err_list.endArray(); - ++it) - { - const LLSD& err_entry = *it; - std::string message = err_entry["message"]; - - if (message.length() > 0) - { - mav_errors.insert(message); - } - - LL_WARNS(LOG_MESH) << " error[" << error_num << "]:" << LL_ENDL; - for (LLSD::map_const_iterator map_it = err_entry.beginMap(); - map_it != err_entry.endMap(); - ++map_it) - { - LL_WARNS(LOG_MESH) << " " << map_it->first << ": " - << map_it->second << LL_ENDL; - } - error_num++; - } - } - } - else - { - LL_WARNS(LOG_MESH) << "Bad response to mesh request, no additional error information available." << LL_ENDL; - } - - mav_errors_set_t::iterator mav_errors_it = mav_errors.begin(); - for (; mav_errors_it != mav_errors.end(); ++mav_errors_it) - { - std::string mav_details = "Mav_Details_" + *mav_errors_it; - details << "Message: '" << *mav_errors_it << "': " << LLTrans::getString(mav_details) << std::endl << std::endl; - } - - std::string details_str = details.str(); - if (details_str.length() > 0) - { - args["DETAILS"] = details_str; - } - - gMeshRepo.uploadError(args); + const char * const stage, const std::string & model_name) +{ + // Add notification popup. + LLSD args; + std::string message = content["error"]["message"].asString(); + std::string identifier = content["error"]["identifier"].asString(); + args["MESSAGE"] = message; + args["IDENTIFIER"] = identifier; + args["LABEL"] = model_name; + + // Log details. + LL_WARNS(LOG_MESH) << "Error in stage: " << stage + << ", Reason: " << status.toString() + << " (" << status.toTerseString() << ")" << LL_ENDL; + + std::ostringstream details; + typedef std::set<std::string> mav_errors_set_t; + mav_errors_set_t mav_errors; + + if (content.has("error")) + { + const LLSD& err = content["error"]; + LL_WARNS(LOG_MESH) << "error: " << err << LL_ENDL; + LL_WARNS(LOG_MESH) << " mesh upload failed, stage '" << stage + << "', error '" << err["error"].asString() + << "', message '" << err["message"].asString() + << "', id '" << err["identifier"].asString() + << "'" << LL_ENDL; + + if (err.has("errors")) + { + details << std::endl << std::endl; + + S32 error_num = 0; + const LLSD& err_list = err["errors"]; + for (LLSD::array_const_iterator it = err_list.beginArray(); + it != err_list.endArray(); + ++it) + { + const LLSD& err_entry = *it; + std::string message = err_entry["message"]; + + if (message.length() > 0) + { + mav_errors.insert(message); + } + + LL_WARNS(LOG_MESH) << " error[" << error_num << "]:" << LL_ENDL; + for (LLSD::map_const_iterator map_it = err_entry.beginMap(); + map_it != err_entry.endMap(); + ++map_it) + { + LL_WARNS(LOG_MESH) << " " << map_it->first << ": " + << map_it->second << LL_ENDL; + } + error_num++; + } + } + } + else + { + LL_WARNS(LOG_MESH) << "Bad response to mesh request, no additional error information available." << LL_ENDL; + } + + mav_errors_set_t::iterator mav_errors_it = mav_errors.begin(); + for (; mav_errors_it != mav_errors.end(); ++mav_errors_it) + { + std::string mav_details = "Mav_Details_" + *mav_errors_it; + details << "Message: '" << *mav_errors_it << "': " << LLTrans::getString(mav_details) << std::endl << std::endl; + } + + std::string details_str = details.str(); + if (details_str.length() > 0) + { + args["DETAILS"] = details_str; + } + + gMeshRepo.uploadError(args); } LLMeshRepoThread::LLMeshRepoThread() @@ -830,36 +830,36 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID) { - LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); - mMutex = new LLMutex(); - mHeaderMutex = new LLMutex(); - mSignal = new LLCondition(); - mHttpRequest = new LLCore::HttpRequest; - mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); - mHttpOptions->setTransferTimeout(SMALL_MESH_XFER_TIMEOUT); - mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); - mHttpLargeOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); - mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); - mHttpLargeOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); - mHttpHeaders = LLCore::HttpHeaders::ptr_t(new 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); + mMutex = new LLMutex(); + mHeaderMutex = new LLMutex(); + mSignal = new LLCondition(); + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + mHttpOptions->setTransferTimeout(SMALL_MESH_XFER_TIMEOUT); + mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); + mHttpLargeOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); + mHttpLargeOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); + mHttpHeaders = LLCore::HttpHeaders::ptr_t(new 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); } LLMeshRepoThread::~LLMeshRepoThread() { - LL_INFOS(LOG_MESH) << "Small GETs issued: " << LLMeshRepository::sHTTPRequestCount - << ", Large GETs issued: " << LLMeshRepository::sHTTPLargeRequestCount - << ", Max Lock Holdoffs: " << LLMeshRepository::sMaxLockHoldoffs - << LL_ENDL; + LL_INFOS(LOG_MESH) << "Small GETs issued: " << LLMeshRepository::sHTTPRequestCount + << ", Large GETs issued: " << LLMeshRepository::sHTTPLargeRequestCount + << ", Max Lock Holdoffs: " << LLMeshRepository::sMaxLockHoldoffs + << LL_ENDL; - mHttpRequestSet.clear(); + mHttpRequestSet.clear(); mHttpHeaders.reset(); - while (!mSkinInfoQ.empty()) + while (!mSkinInfoQ.empty()) { delete mSkinInfoQ.front(); mSkinInfoQ.pop_front(); @@ -872,56 +872,56 @@ LLMeshRepoThread::~LLMeshRepoThread() } delete mHttpRequest; - mHttpRequest = NULL; - delete mMutex; - mMutex = NULL; - delete mHeaderMutex; - mHeaderMutex = NULL; - delete mSignal; - mSignal = NULL; + mHttpRequest = NULL; + delete mMutex; + mMutex = NULL; + delete mHeaderMutex; + mHeaderMutex = NULL; + delete mSignal; + mSignal = NULL; } void LLMeshRepoThread::run() { - LLCDResult res = LLConvexDecomposition::initThread(); - if (res != LLCD_OK && LLConvexDecomposition::isFunctional()) - { - LL_WARNS(LOG_MESH) << "Convex decomposition unable to be loaded. Expect severe problems." << LL_ENDL; - } - - while (!LLApp::isExiting()) - { - // *TODO: Revise sleep/wake strategy and try to move away - // from polling operations in this thread. We can sleep - // this thread hard when: - // * All Http requests are serviced - // * LOD request queue empty - // * Header request queue empty - // * Skin info request queue empty - // * Decomposition request queue empty - // * Physics shape request queue empty - // We wake the thread when any of the above become untrue. - // Will likely need a correctly-implemented condition variable to do this. - // On the other hand, this may actually be an effective and efficient scheme... - - mSignal->wait(); - - if (LLApp::isExiting()) - { - break; - } - - if (! mHttpRequestSet.empty()) - { - // Dispatch all HttpHandler notifications - mHttpRequest->update(0L); - } - sRequestWaterLevel = mHttpRequestSet.size(); // Stats data update - - // NOTE: order of queue processing intentionally favors LOD 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 + LLCDResult res = LLConvexDecomposition::initThread(); + if (res != LLCD_OK && LLConvexDecomposition::isFunctional()) + { + LL_WARNS(LOG_MESH) << "Convex decomposition unable to be loaded. Expect severe problems." << LL_ENDL; + } + + while (!LLApp::isExiting()) + { + // *TODO: Revise sleep/wake strategy and try to move away + // from polling operations in this thread. We can sleep + // this thread hard when: + // * All Http requests are serviced + // * LOD request queue empty + // * Header request queue empty + // * Skin info request queue empty + // * Decomposition request queue empty + // * Physics shape request queue empty + // We wake the thread when any of the above become untrue. + // Will likely need a correctly-implemented condition variable to do this. + // On the other hand, this may actually be an effective and efficient scheme... + + mSignal->wait(); + + if (LLApp::isExiting()) + { + break; + } + + if (! mHttpRequestSet.empty()) + { + // Dispatch all HttpHandler notifications + mHttpRequest->update(0L); + } + sRequestWaterLevel = mHttpRequestSet.size(); // Stats data update + + // NOTE: order of queue processing intentionally favors LOD 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 (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) { @@ -954,7 +954,7 @@ void LLMeshRepoThread::run() else { // too many fails - LLMutexLock lock(mMutex); + LLMutexLock lock(mMutex); mUnavailableQ.push_back(req); LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL; } @@ -1031,42 +1031,42 @@ void LLMeshRepoThread::run() 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); - } - } + 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 @@ -1145,87 +1145,87 @@ void LLMeshRepoThread::run() } } - // For dev purposes only. A dynamic change could make this false - // and that shouldn't assert. - // llassert_always(mHttpRequestSet.size() <= sRequestHighWater); - } - - if (mSignal->isLocked()) - { //make sure to let go of the mutex associated with the given signal before shutting down - mSignal->unlock(); - } + // For dev purposes only. A dynamic change could make this false + // and that shouldn't assert. + // llassert_always(mHttpRequestSet.size() <= sRequestHighWater); + } + + if (mSignal->isLocked()) + { //make sure to let go of the mutex associated with the given signal before shutting down + mSignal->unlock(); + } - res = LLConvexDecomposition::quitThread(); - if (res != LLCD_OK && LLConvexDecomposition::isFunctional()) - { - LL_WARNS(LOG_MESH) << "Convex decomposition unable to be quit." << LL_ENDL; - } + res = LLConvexDecomposition::quitThread(); + if (res != LLCD_OK && LLConvexDecomposition::isFunctional()) + { + LL_WARNS(LOG_MESH) << "Convex decomposition unable to be quit." << LL_ENDL; + } } // Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id) { - mSkinRequests.push_back(UUIDBasedRequest(mesh_id)); + mSkinRequests.push_back(UUIDBasedRequest(mesh_id)); } // Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id) { - mDecompositionRequests.insert(UUIDBasedRequest(mesh_id)); + mDecompositionRequests.insert(UUIDBasedRequest(mesh_id)); } // Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id) { - mPhysicsShapeRequests.insert(UUIDBasedRequest(mesh_id)); + mPhysicsShapeRequests.insert(UUIDBasedRequest(mesh_id)); } void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { - if (!LLAppViewer::isExiting()) - { - loadMeshLOD(mesh_params, lod); - } + if (!LLAppViewer::isExiting()) + { + loadMeshLOD(mesh_params, lod); + } } 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()) - { //if we have the header, request LOD byte range - - LODRequest req(mesh_params, lod); - { - mLODReqQ.push(req); - LLMeshRepository::sLODProcessing++; - } - } - else - { - 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); - } - else - { //if no header request is pending, fetch header - mHeaderReqQ.push(req); - mPendingLOD[mesh_id].push_back(lod); - } - } + 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()) + { //if we have the header, request LOD byte range + + LODRequest req(mesh_params, lod); + { + mLODReqQ.push(req); + LLMeshRepository::sLODProcessing++; + } + } + else + { + 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); + } + else + { //if no header request is pending, fetch header + mHeaderReqQ.push(req); + mPendingLOD[mesh_id].push_back(lod); + } + } } // Mutex: must be holding mMutex when called void LLMeshRepoThread::setGetMeshCap(const std::string & mesh_cap) { - mGetMeshCapability = mesh_cap; + mGetMeshCapability = mesh_cap; } @@ -1235,740 +1235,740 @@ void LLMeshRepoThread::setGetMeshCap(const std::string & mesh_cap) // Mutex: acquires mMutex void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url) { - std::string res_url; - - if (gAgent.getRegion()) - { - { - LLMutexLock lock(mMutex); - res_url = mGetMeshCapability; - } - - if (!res_url.empty()) - { - res_url += "/?mesh_id="; - res_url += mesh_id.asString().c_str(); - } - else - { - LL_WARNS_ONCE(LOG_MESH) << "Current region does not have ViewerAsset capability! Cannot load meshes. Region id: " - << gAgent.getRegion()->getRegionID() << LL_ENDL; - LL_DEBUGS_ONCE(LOG_MESH) << "Cannot load mesh " << mesh_id << " due to missing capability." << LL_ENDL; - } - } - else - { - LL_WARNS_ONCE(LOG_MESH) << "Current region is not loaded so there is no capability to load from! Cannot load meshes." << LL_ENDL; - LL_DEBUGS_ONCE(LOG_MESH) << "Cannot load mesh " << mesh_id << " due to missing capability." << LL_ENDL; - } - - *url = res_url; + std::string res_url; + + if (gAgent.getRegion()) + { + { + LLMutexLock lock(mMutex); + res_url = mGetMeshCapability; + } + + if (!res_url.empty()) + { + res_url += "/?mesh_id="; + res_url += mesh_id.asString().c_str(); + } + else + { + LL_WARNS_ONCE(LOG_MESH) << "Current region does not have ViewerAsset capability! Cannot load meshes. Region id: " + << gAgent.getRegion()->getRegionID() << LL_ENDL; + LL_DEBUGS_ONCE(LOG_MESH) << "Cannot load mesh " << mesh_id << " due to missing capability." << LL_ENDL; + } + } + else + { + LL_WARNS_ONCE(LOG_MESH) << "Current region is not loaded so there is no capability to load from! Cannot load meshes." << LL_ENDL; + LL_DEBUGS_ONCE(LOG_MESH) << "Cannot load mesh " << mesh_id << " due to missing capability." << LL_ENDL; + } + + *url = res_url; } // Issue an HTTP GET request with byte range using the right -// policy class. +// policy class. // -// @return Valid handle or LLCORE_HTTP_HANDLE_INVALID. -// If the latter, actual status is found in -// mHttpStatus member which is valid until the -// next call to this method. +// @return Valid handle or LLCORE_HTTP_HANDLE_INVALID. +// If the latter, actual status is found in +// mHttpStatus member which is valid until the +// next call to this method. // // Thread: repo LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, - size_t offset, size_t len, - const LLCore::HttpHandler::ptr_t &handler) -{ - // Also used in lltexturefetch.cpp - static LLCachedControl<bool> disable_range_req(gSavedSettings, "HttpRangeRequestsDisable", false); - - LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - - if (len < LARGE_MESH_FETCH_THRESHOLD) - { - handle = mHttpRequest->requestGetByteRange( mHttpPolicyClass, + size_t offset, size_t len, + const LLCore::HttpHandler::ptr_t &handler) +{ + // Also used in lltexturefetch.cpp + static LLCachedControl<bool> disable_range_req(gSavedSettings, "HttpRangeRequestsDisable", false); + + LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + if (len < LARGE_MESH_FETCH_THRESHOLD) + { + handle = mHttpRequest->requestGetByteRange( mHttpPolicyClass, url, (disable_range_req ? size_t(0) : offset), (disable_range_req ? size_t(0) : len), mHttpOptions, mHttpHeaders, handler); - if (LLCORE_HTTP_HANDLE_INVALID != handle) - { - ++LLMeshRepository::sHTTPRequestCount; - } - } - else - { - handle = mHttpRequest->requestGetByteRange(mHttpLargePolicyClass, - url, - (disable_range_req ? size_t(0) : offset), - (disable_range_req ? size_t(0) : len), - mHttpLargeOptions, - mHttpHeaders, - handler); - if (LLCORE_HTTP_HANDLE_INVALID != handle) - { - ++LLMeshRepository::sHTTPLargeRequestCount; - } - } - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - // Something went wrong, capture the error code for caller. - mHttpStatus = mHttpRequest->getStatus(); - } - return handle; + if (LLCORE_HTTP_HANDLE_INVALID != handle) + { + ++LLMeshRepository::sHTTPRequestCount; + } + } + else + { + handle = mHttpRequest->requestGetByteRange(mHttpLargePolicyClass, + url, + (disable_range_req ? size_t(0) : offset), + (disable_range_req ? size_t(0) : len), + mHttpLargeOptions, + mHttpHeaders, + handler); + if (LLCORE_HTTP_HANDLE_INVALID != handle) + { + ++LLMeshRepository::sHTTPLargeRequestCount; + } + } + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + // Something went wrong, capture the error code for caller. + mHttpStatus = mHttpRequest->getStatus(); + } + return handle; } bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) { - - if (!mHeaderMutex) - { - return false; - } - - mHeaderMutex->lock(); - - auto header_it = mMeshHeader.find(mesh_id); - if (header_it == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing - mHeaderMutex->unlock(); - return false; - } - - ++LLMeshRepository::sMeshRequestCount; - bool ret = true; - U32 header_size = header_it->second.first; - - if (header_size > 0) - { - const LLMeshHeader& header = header_it->second.second; + + if (!mHeaderMutex) + { + return false; + } + + mHeaderMutex->lock(); + + auto header_it = mMeshHeader.find(mesh_id); + if (header_it == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } + + ++LLMeshRepository::sMeshRequestCount; + bool ret = true; + U32 header_size = header_it->second.first; + + 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; - - mHeaderMutex->unlock(); - - if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) - { - //check cache for mesh skin info - LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) - { - U8* buffer = new(std::nothrow) U8[size]; - if (!buffer) - { - LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL; - return false; - } - LLMeshRepository::sCacheBytesRead += size; - ++LLMeshRepository::sCacheReads; - file.seek(offset); - file.read(buffer, size); - - //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) - bool zero = true; - for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) - { - zero = buffer[i] > 0 ? false : true; - } - - if (!zero) - { //attempt to parse - if (skinInfoReceived(mesh_id, buffer, size)) - { - delete[] buffer; - return true; - } - } - - delete[] buffer; - } - - //reading from cache failed for whatever reason, fetch from sim - std::string http_url; - constructUrl(mesh_id, &http_url); - - if (!http_url.empty()) - { + S32 offset = header_size + header.mSkinOffset; + S32 size = header.mSkinSize; + + mHeaderMutex->unlock(); + + if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) + { + //check cache for mesh skin info + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + U8* buffer = new(std::nothrow) U8[size]; + if (!buffer) + { + LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL; + return false; + } + LLMeshRepository::sCacheBytesRead += size; + ++LLMeshRepository::sCacheReads; + file.seek(offset); + file.read(buffer, size); + + //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (skinInfoReceived(mesh_id, buffer, size)) + { + delete[] buffer; + return true; + } + } + + delete[] buffer; + } + + //reading from cache failed for whatever reason, fetch from sim + std::string http_url; + constructUrl(mesh_id, &http_url); + + if (!http_url.empty()) + { LLMeshHandlerBase::ptr_t handler(new LLMeshSkinInfoHandler(mesh_id, offset, size)); - LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - LL_WARNS(LOG_MESH) << "HTTP GET request failed for skin info on mesh " << mID - << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toTerseString() << ")" - << LL_ENDL; - ret = false; - } - else if(can_retry) - { - handler->mHttpHandle = handle; - mHttpRequestSet.insert(handler); - } - else - { - LLMutexLock locker(mMutex); - mSkinUnavailableQ.emplace_back(mesh_id); - } - } - else - { - LLMutexLock locker(mMutex); - mSkinUnavailableQ.emplace_back(mesh_id); - } - } - else - { - LLMutexLock locker(mMutex); - mSkinUnavailableQ.emplace_back(mesh_id); - } - } - else - { - mHeaderMutex->unlock(); - } - - //early out was not hit, effectively fetched - return ret; + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + LL_WARNS(LOG_MESH) << "HTTP GET request failed for skin info on mesh " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toTerseString() << ")" + << LL_ENDL; + ret = false; + } + else if(can_retry) + { + handler->mHttpHandle = handle; + mHttpRequestSet.insert(handler); + } + else + { + LLMutexLock locker(mMutex); + mSkinUnavailableQ.emplace_back(mesh_id); + } + } + else + { + LLMutexLock locker(mMutex); + mSkinUnavailableQ.emplace_back(mesh_id); + } + } + else + { + LLMutexLock locker(mMutex); + mSkinUnavailableQ.emplace_back(mesh_id); + } + } + else + { + mHeaderMutex->unlock(); + } + + //early out was not hit, effectively fetched + return ret; } bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { - if (!mHeaderMutex) - { - return false; - } - - mHeaderMutex->lock(); - - auto header_it = mMeshHeader.find(mesh_id); - if (header_it == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing - mHeaderMutex->unlock(); - return false; - } - - ++LLMeshRepository::sMeshRequestCount; - U32 header_size = header_it->second.first; - bool ret = true; - - if (header_size > 0) - { - const auto& header = header_it->second.second; + if (!mHeaderMutex) + { + return false; + } + + mHeaderMutex->lock(); + + auto header_it = mMeshHeader.find(mesh_id); + if (header_it == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } + + ++LLMeshRepository::sMeshRequestCount; + U32 header_size = header_it->second.first; + 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; - mHeaderMutex->unlock(); - - if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) - { - //check cache for mesh skin info - LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) - { - U8* buffer = new(std::nothrow) U8[size]; - if (!buffer) - { - LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL; - return false; - } - LLMeshRepository::sCacheBytesRead += size; - ++LLMeshRepository::sCacheReads; - - file.seek(offset); - file.read(buffer, size); - - //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) - bool zero = true; - for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) - { - zero = buffer[i] > 0 ? false : true; - } - - if (!zero) - { //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 - std::string http_url; - constructUrl(mesh_id, &http_url); - - if (!http_url.empty()) - { + mHeaderMutex->unlock(); + + if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) + { + //check cache for mesh skin info + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + U8* buffer = new(std::nothrow) U8[size]; + if (!buffer) + { + LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL; + return false; + } + LLMeshRepository::sCacheBytesRead += size; + ++LLMeshRepository::sCacheReads; + + file.seek(offset); + file.read(buffer, size); + + //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //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 + std::string http_url; + constructUrl(mesh_id, &http_url); + + if (!http_url.empty()) + { LLMeshHandlerBase::ptr_t handler(new LLMeshDecompositionHandler(mesh_id, offset, size)); - LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - LL_WARNS(LOG_MESH) << "HTTP GET request failed for decomposition mesh " << mID - << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toTerseString() << ")" - << LL_ENDL; - ret = false; - } - else - { - handler->mHttpHandle = handle; - mHttpRequestSet.insert(handler); - } - } - } - } - else - { - mHeaderMutex->unlock(); - } - - //early out was not hit, effectively fetched - return ret; + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + LL_WARNS(LOG_MESH) << "HTTP GET request failed for decomposition mesh " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toTerseString() << ")" + << LL_ENDL; + ret = false; + } + else + { + handler->mHttpHandle = handle; + mHttpRequestSet.insert(handler); + } + } + } + } + else + { + mHeaderMutex->unlock(); + } + + //early out was not hit, effectively fetched + return ret; } bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { - if (!mHeaderMutex) - { - return false; - } + if (!mHeaderMutex) + { + return false; + } - mHeaderMutex->lock(); + mHeaderMutex->lock(); - auto header_it = mMeshHeader.find(mesh_id); - if (header_it == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing - mHeaderMutex->unlock(); - return false; - } + auto header_it = mMeshHeader.find(mesh_id); + if (header_it == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } - ++LLMeshRepository::sMeshRequestCount; - U32 header_size = header_it->second.first; - bool ret = true; + ++LLMeshRepository::sMeshRequestCount; + U32 header_size = header_it->second.first; + bool ret = true; - if (header_size > 0) - { - const auto& header = header_it->second.second; + 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; - - mHeaderMutex->unlock(); - - if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) - { - //check cache for mesh physics shape info - LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesRead += size; - ++LLMeshRepository::sCacheReads; - file.seek(offset); - U8* buffer = new(std::nothrow) U8[size]; - if (!buffer) - { - LL_WARNS(LOG_MESH) << "Failed to allocate memory for physics shape, size: " << size << LL_ENDL; - return false; - } - file.read(buffer, size); - - //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) - bool zero = true; - for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) - { - zero = buffer[i] > 0 ? false : true; - } - - if (!zero) - { //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 - std::string http_url; - constructUrl(mesh_id, &http_url); - - if (!http_url.empty()) - { + S32 size = header.mPhysicsMeshSize; + + mHeaderMutex->unlock(); + + if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) + { + //check cache for mesh physics shape info + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesRead += size; + ++LLMeshRepository::sCacheReads; + file.seek(offset); + U8* buffer = new(std::nothrow) U8[size]; + if (!buffer) + { + LL_WARNS(LOG_MESH) << "Failed to allocate memory for physics shape, size: " << size << LL_ENDL; + return false; + } + file.read(buffer, size); + + //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //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 + std::string http_url; + constructUrl(mesh_id, &http_url); + + if (!http_url.empty()) + { LLMeshHandlerBase::ptr_t handler(new LLMeshPhysicsShapeHandler(mesh_id, offset, size)); - LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - LL_WARNS(LOG_MESH) << "HTTP GET request failed for physics shape on mesh " << mID - << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toTerseString() << ")" - << LL_ENDL; - ret = false; - } - else - { - handler->mHttpHandle = handle; - mHttpRequestSet.insert(handler); - } - } - } - else - { //no physics shape whatsoever, report back NULL - physicsShapeReceived(mesh_id, NULL, 0); - } - } - else - { - mHeaderMutex->unlock(); - } - - //early out was not hit, effectively fetched - return ret; + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + LL_WARNS(LOG_MESH) << "HTTP GET request failed for physics shape on mesh " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toTerseString() << ")" + << LL_ENDL; + ret = false; + } + else + { + handler->mHttpHandle = handle; + mHttpRequestSet.insert(handler); + } + } + } + else + { //no physics shape whatsoever, report back NULL + physicsShapeReceived(mesh_id, NULL, 0); + } + } + else + { + mHeaderMutex->unlock(); + } + + //early out was not hit, effectively fetched + return ret; } //static void LLMeshRepoThread::incActiveLODRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); - ++LLMeshRepoThread::sActiveLODRequests; + LLMutexLock lock(gMeshRepo.mThread->mMutex); + ++LLMeshRepoThread::sActiveLODRequests; } //static void LLMeshRepoThread::decActiveLODRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); - --LLMeshRepoThread::sActiveLODRequests; + LLMutexLock lock(gMeshRepo.mThread->mMutex); + --LLMeshRepoThread::sActiveLODRequests; } //static void LLMeshRepoThread::incActiveHeaderRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); - ++LLMeshRepoThread::sActiveHeaderRequests; + LLMutexLock lock(gMeshRepo.mThread->mMutex); + ++LLMeshRepoThread::sActiveHeaderRequests; } //static void LLMeshRepoThread::decActiveHeaderRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); - --LLMeshRepoThread::sActiveHeaderRequests; + LLMutexLock lock(gMeshRepo.mThread->mMutex); + --LLMeshRepoThread::sActiveHeaderRequests; } //return false if failed to get header bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry) { - ++LLMeshRepository::sMeshRequestCount; - - { - //look for mesh in asset in cache - LLFileSystem file(mesh_params.getSculptID(), LLAssetType::AT_MESH); - - S32 size = file.getSize(); - - 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); - LLMeshRepository::sCacheBytesRead += bytes; - ++LLMeshRepository::sCacheReads; - file.read(buffer, bytes); - if (headerReceived(mesh_params, buffer, bytes) == MESH_OK) - { - std::string mid; - mesh_params.getSculptID().toString(mid); - LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mid << " - was retrieved from the cache." << LL_ENDL; - - // Found mesh in cache - return true; - } - } - } - - //either cache entry doesn't exist or is corrupt, request header from simulator - bool retval = true; - std::string http_url; - constructUrl(mesh_params.getSculptID(), &http_url); - - - if (!http_url.empty()) - { - std::string mid; - mesh_params.getSculptID().toString(mid); - LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mid << " - was retrieved from the simulator." << LL_ENDL; - - //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits - //within the first 4KB - //NOTE -- this will break of headers ever exceed 4KB + ++LLMeshRepository::sMeshRequestCount; + + { + //look for mesh in asset in cache + LLFileSystem file(mesh_params.getSculptID(), LLAssetType::AT_MESH); + + S32 size = file.getSize(); + + 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); + LLMeshRepository::sCacheBytesRead += bytes; + ++LLMeshRepository::sCacheReads; + file.read(buffer, bytes); + if (headerReceived(mesh_params, buffer, bytes) == MESH_OK) + { + std::string mid; + mesh_params.getSculptID().toString(mid); + LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mid << " - was retrieved from the cache." << LL_ENDL; + + // Found mesh in cache + return true; + } + } + } + + //either cache entry doesn't exist or is corrupt, request header from simulator + bool retval = true; + std::string http_url; + constructUrl(mesh_params.getSculptID(), &http_url); + + + if (!http_url.empty()) + { + std::string mid; + mesh_params.getSculptID().toString(mid); + LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mid << " - was retrieved from the simulator." << LL_ENDL; + + //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits + //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)); - LLCore::HttpHandle handle = getByteRange(http_url, 0, MESH_HEADER_SIZE, handler); - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - LL_WARNS(LOG_MESH) << "HTTP GET request failed for mesh header " << mID - << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toTerseString() << ")" - << LL_ENDL; - retval = false; - } - else if (can_retry) - { - handler->mHttpHandle = handle; - mHttpRequestSet.insert(handler); - } - } - - return retval; + LLCore::HttpHandle handle = getByteRange(http_url, 0, MESH_HEADER_SIZE, handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + LL_WARNS(LOG_MESH) << "HTTP GET request failed for mesh header " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toTerseString() << ")" + << LL_ENDL; + retval = false; + } + else if (can_retry) + { + handler->mHttpHandle = handle; + mHttpRequestSet.insert(handler); + } + } + + return retval; } //return false if failed to get mesh lod. bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry) { - if (!mHeaderMutex) - { - return false; - } - - const LLUUID& mesh_id = mesh_params.getSculptID(); - - mHeaderMutex->lock(); - auto header_it = mMeshHeader.find(mesh_id); - if (header_it == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing - mHeaderMutex->unlock(); - return false; - } - ++LLMeshRepository::sMeshRequestCount; - bool retval = true; - - U32 header_size = header_it->second.first; - if (header_size > 0) - { - const auto& header = header_it->second.second; + if (!mHeaderMutex) + { + return false; + } + + const LLUUID& mesh_id = mesh_params.getSculptID(); + + mHeaderMutex->lock(); + auto header_it = mMeshHeader.find(mesh_id); + if (header_it == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } + ++LLMeshRepository::sMeshRequestCount; + bool retval = true; + + U32 header_size = header_it->second.first; + 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]; - mHeaderMutex->unlock(); - - if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) - { - - //check cache for mesh asset - LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) - { - U8* buffer = new(std::nothrow) U8[size]; - if (!buffer) - { - LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL; - // todo: for now it will result in indefinite constant retries, should result in timeout - // or in retry-count and disabling mesh. (but usually viewer is beyond saving at this point) - return false; - } - LLMeshRepository::sCacheBytesRead += size; - ++LLMeshRepository::sCacheReads; - file.seek(offset); - file.read(buffer, size); - - //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) - bool zero = true; - for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) - { - zero = buffer[i] > 0 ? false : true; - } - - if (!zero) - { //attempt to parse - if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK) - { - delete[] buffer; - - std::string mid; - mesh_id.toString(mid); - LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mid << " - was retrieved from the cache." << LL_ENDL; - - return true; - } - } - - delete[] buffer; - } - - //reading from cache failed for whatever reason, fetch from sim - std::string http_url; - constructUrl(mesh_id, &http_url); - - if (!http_url.empty()) - { - std::string mid; - mesh_id.toString(mid); - LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mid << " - was retrieved from the simulator." << LL_ENDL; + mHeaderMutex->unlock(); + + if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) + { + + //check cache for mesh asset + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + U8* buffer = new(std::nothrow) U8[size]; + if (!buffer) + { + LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL; + // todo: for now it will result in indefinite constant retries, should result in timeout + // or in retry-count and disabling mesh. (but usually viewer is beyond saving at this point) + return false; + } + LLMeshRepository::sCacheBytesRead += size; + ++LLMeshRepository::sCacheReads; + file.seek(offset); + file.read(buffer, size); + + //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK) + { + delete[] buffer; + + std::string mid; + mesh_id.toString(mid); + LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mid << " - was retrieved from the cache." << LL_ENDL; + + return true; + } + } + + delete[] buffer; + } + + //reading from cache failed for whatever reason, fetch from sim + std::string http_url; + constructUrl(mesh_id, &http_url); + + if (!http_url.empty()) + { + std::string mid; + mesh_id.toString(mid); + LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mid << " - was retrieved from the simulator." << LL_ENDL; LLMeshHandlerBase::ptr_t handler(new LLMeshLODHandler(mesh_params, lod, offset, size)); - LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - LL_WARNS(LOG_MESH) << "HTTP GET request failed for LOD on mesh " << mID - << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toTerseString() << ")" - << LL_ENDL; - retval = false; - } - else if (can_retry) - { - 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); - mUnavailableQ.push_back(LODRequest(mesh_params, lod)); - } - } - else - { - LLMutexLock lock(mMutex); - mUnavailableQ.push_back(LODRequest(mesh_params, lod)); - } - } - else - { - mHeaderMutex->unlock(); - } - - return retval; + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + LL_WARNS(LOG_MESH) << "HTTP GET request failed for LOD on mesh " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toTerseString() << ")" + << LL_ENDL; + retval = false; + } + else if (can_retry) + { + 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); + mUnavailableQ.push_back(LODRequest(mesh_params, lod)); + } + } + else + { + LLMutexLock lock(mMutex); + mUnavailableQ.push_back(LODRequest(mesh_params, lod)); + } + } + else + { + mHeaderMutex->unlock(); + } + + return retval; } EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) { - const LLUUID mesh_id = mesh_params.getSculptID(); - LLSD header_data; - + const LLUUID mesh_id = mesh_params.getSculptID(); + LLSD header_data; + LLMeshHeader header; - - llssize header_size = 0; - if (data_size > 0) - { - llssize dsize = data_size; - char* result_ptr = strip_deprecated_header((char*)data, dsize, &header_size); - - data_size = dsize; - - boost::iostreams::stream<boost::iostreams::array_source> stream(result_ptr, data_size); - - if (!LLSDSerialize::fromBinary(header_data, stream, data_size)) - { - LL_WARNS(LOG_MESH) << "Mesh header parse error. Not a valid mesh asset! ID: " << mesh_id - << LL_ENDL; - return MESH_PARSE_FAILURE; - } - - if (!header_data.isMap()) - { - LL_WARNS(LOG_MESH) << "Mesh header is invalid for ID: " << mesh_id << LL_ENDL; - return MESH_INVALID; - } + + llssize header_size = 0; + if (data_size > 0) + { + llssize dsize = data_size; + char* result_ptr = strip_deprecated_header((char*)data, dsize, &header_size); + + data_size = dsize; + + boost::iostreams::stream<boost::iostreams::array_source> stream(result_ptr, data_size); + + if (!LLSDSerialize::fromBinary(header_data, stream, data_size)) + { + LL_WARNS(LOG_MESH) << "Mesh header parse error. Not a valid mesh asset! ID: " << mesh_id + << LL_ENDL; + return MESH_PARSE_FAILURE; + } + + if (!header_data.isMap()) + { + LL_WARNS(LOG_MESH) << "Mesh header is invalid for ID: " << mesh_id << LL_ENDL; + return MESH_INVALID; + } header.fromLLSD(header_data); - if (header.mVersion > MAX_MESH_VERSION) - { - LL_INFOS(LOG_MESH) << "Wrong version in header for " << mesh_id << LL_ENDL; - header.m404 = true; - } - // 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(); - } - } - else - { - LL_INFOS(LOG_MESH) << "Non-positive data size. Marking header as non-existent, will not retry. ID: " << mesh_id - << LL_ENDL; - header.m404 = 1; - } - - { - - { - LLMutexLock lock(mHeaderMutex); - mMeshHeader[mesh_id] = { header_size, header }; + if (header.mVersion > MAX_MESH_VERSION) + { + LL_INFOS(LOG_MESH) << "Wrong version in header for " << mesh_id << LL_ENDL; + header.m404 = true; + } + // 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(); + } + } + else + { + LL_INFOS(LOG_MESH) << "Non-positive data size. Marking header as non-existent, will not retry. ID: " << mesh_id + << LL_ENDL; + header.m404 = 1; + } + + { + + { + LLMutexLock lock(mHeaderMutex); + mMeshHeader[mesh_id] = { header_size, header }; LLMeshRepository::sCacheBytesHeaders += header_size; - } - - LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. - - //check for pending requests - pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); - if (iter != mPendingLOD.end()) - { - for (U32 i = 0; i < iter->second.size(); ++i) - { - LODRequest req(mesh_params, iter->second[i]); - mLODReqQ.push(req); - LLMeshRepository::sLODProcessing++; - } - mPendingLOD.erase(iter); - } - } - - return MESH_OK; + } + + LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. + + //check for pending requests + pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); + if (iter != mPendingLOD.end()) + { + for (U32 i = 0; i < iter->second.size(); ++i) + { + LODRequest req(mesh_params, iter->second[i]); + mLODReqQ.push(req); + LLMeshRepository::sLODProcessing++; + } + mPendingLOD.erase(iter); + } + } + + return MESH_OK; } EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size) { - if (data == NULL || data_size == 0) - { - return MESH_NO_DATA; - } - - LLPointer<LLVolume> volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod)); - if (volume->unpackVolumeFaces(data, data_size)) - { - if (volume->getNumFaces() > 0) - { - LoadedMesh mesh(volume, mesh_params, lod); - { - LLMutexLock lock(mMutex); - 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 - // and won't affect mLoadedQ processing - volume = NULL; - // might be good idea to turn mesh into pointer to avoid making a copy - mesh.mVolume = NULL; - } - return MESH_OK; - } - } - - return MESH_UNKNOWN; + if (data == NULL || data_size == 0) + { + return MESH_NO_DATA; + } + + LLPointer<LLVolume> volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod)); + if (volume->unpackVolumeFaces(data, data_size)) + { + if (volume->getNumFaces() > 0) + { + LoadedMesh mesh(volume, mesh_params, lod); + { + LLMutexLock lock(mMutex); + 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 + // and won't affect mLoadedQ processing + volume = NULL; + // might be good idea to turn mesh into pointer to avoid making a copy + mesh.mVolume = NULL; + } + return MESH_OK; + } + } + + return MESH_UNKNOWN; } bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { - LLSD skin; + LLSD skin; - if (data_size > 0) - { + if (data_size > 0) + { try { U32 uzip_result = LLUZipHelper::unzip_llsd(skin, data, data_size); @@ -1985,35 +1985,35 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat LL_WARNS(LOG_MESH) << "Out of memory for mesh ID " << mesh_id << " of size: " << data_size << LL_ENDL; return false; } - } - - { - LLMeshSkinInfo* info = nullptr; - try - { - info = new LLMeshSkinInfo(mesh_id, skin); - } - catch (const std::bad_alloc& ex) - { - LL_WARNS() << "Failed to allocate skin info with exception: " << ex.what() << LL_ENDL; - return false; - } + } + + { + LLMeshSkinInfo* info = nullptr; + try + { + info = new LLMeshSkinInfo(mesh_id, skin); + } + catch (const std::bad_alloc& ex) + { + LL_WARNS() << "Failed to allocate skin info with exception: " << ex.what() << LL_ENDL; + return false; + } // LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL; - { - LLMutexLock lock(mMutex); - mSkinInfoQ.push_back(info); - } - } + { + LLMutexLock lock(mMutex); + mSkinInfoQ.push_back(info); + } + } - return true; + return true; } bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { - LLSD decomp; + LLSD decomp; - if (data_size > 0) + if (data_size > 0) { try { @@ -2031,1006 +2031,1006 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 LL_WARNS(LOG_MESH) << "Out of memory for mesh ID " << mesh_id << " of size: " << data_size << LL_ENDL; return false; } - } - - { - LLModel::Decomposition* d = new LLModel::Decomposition(decomp); - d->mMeshID = mesh_id; - { - LLMutexLock lock(mMutex); - mDecompositionQ.push_back(d); - } - } + } + + { + LLModel::Decomposition* d = new LLModel::Decomposition(decomp); + d->mMeshID = mesh_id; + { + LLMutexLock lock(mMutex); + mDecompositionQ.push_back(d); + } + } - return true; + return true; } EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { - LLSD physics_shape; - - LLModel::Decomposition* d = new LLModel::Decomposition(); - d->mMeshID = mesh_id; - - if (data == NULL) - { //no data, no physics shape exists - d->mPhysicsShapeMesh.clear(); - } - else - { - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH); - LLPointer<LLVolume> volume = new LLVolume(volume_params,0); - - if (volume->unpackVolumeFaces(data, data_size)) - { - d->mPhysicsShapeMesh.clear(); - - std::vector<LLVector3>& pos = d->mPhysicsShapeMesh.mPositions; - std::vector<LLVector3>& norm = d->mPhysicsShapeMesh.mNormals; - - for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i) - { - const LLVolumeFace& face = volume->getVolumeFace(i); - - for (S32 i = 0; i < face.mNumIndices; ++i) - { - U16 idx = face.mIndices[i]; - - pos.push_back(LLVector3(face.mPositions[idx].getF32ptr())); - norm.push_back(LLVector3(face.mNormals[idx].getF32ptr())); - } - } - } - } - - { - LLMutexLock lock(mMutex); - mDecompositionQ.push_back(d); - } - return MESH_OK; + LLSD physics_shape; + + LLModel::Decomposition* d = new LLModel::Decomposition(); + d->mMeshID = mesh_id; + + if (data == NULL) + { //no data, no physics shape exists + d->mPhysicsShapeMesh.clear(); + } + else + { + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH); + LLPointer<LLVolume> volume = new LLVolume(volume_params,0); + + if (volume->unpackVolumeFaces(data, data_size)) + { + d->mPhysicsShapeMesh.clear(); + + std::vector<LLVector3>& pos = d->mPhysicsShapeMesh.mPositions; + std::vector<LLVector3>& norm = d->mPhysicsShapeMesh.mNormals; + + for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = volume->getVolumeFace(i); + + for (S32 i = 0; i < face.mNumIndices; ++i) + { + U16 idx = face.mIndices[i]; + + pos.push_back(LLVector3(face.mPositions[idx].getF32ptr())); + norm.push_back(LLVector3(face.mNormals[idx].getF32ptr())); + } + } + } + } + + { + LLMutexLock lock(mMutex); + mDecompositionQ.push_back(d); + } + return MESH_OK; } LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, - bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, + bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, const std::string & upload_url, bool do_upload, - LLHandle<LLWholeModelFeeObserver> fee_observer, - LLHandle<LLWholeModelUploadObserver> upload_observer) + LLHandle<LLWholeModelFeeObserver> fee_observer, + LLHandle<LLWholeModelUploadObserver> upload_observer) : LLThread("mesh upload"), - LLCore::HttpHandler(), - mDiscarded(false), - mDoUpload(do_upload), - mWholeModelUploadURL(upload_url), - mFeeObserverHandle(fee_observer), - mUploadObserverHandle(upload_observer) -{ - mInstanceList = data; - mUploadTextures = upload_textures; - mUploadSkin = upload_skin; - mUploadJoints = upload_joints; + LLCore::HttpHandler(), + mDiscarded(false), + mDoUpload(do_upload), + mWholeModelUploadURL(upload_url), + mFeeObserverHandle(fee_observer), + mUploadObserverHandle(upload_observer) +{ + mInstanceList = data; + mUploadTextures = upload_textures; + mUploadSkin = upload_skin; + mUploadJoints = upload_joints; mLockScaleIfJointPosition = lock_scale_if_joint_position; - mMutex = new LLMutex(); - mPendingUploads = 0; - mFinished = false; - mOrigin = gAgent.getPositionAgent(); - mHost = gAgent.getRegionHost(); - - mWholeModelFeeCapability = gAgent.getRegionCapability("NewFileAgentInventory"); + mMutex = new LLMutex(); + mPendingUploads = 0; + mFinished = false; + mOrigin = gAgent.getPositionAgent(); + mHost = gAgent.getRegionHost(); + + mWholeModelFeeCapability = gAgent.getRegionCapability("NewFileAgentInventory"); - mOrigin += gAgent.getAtAxis() * scale.magVec(); + mOrigin += gAgent.getAtAxis() * scale.magVec(); - mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut"); + mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut"); - mHttpRequest = new LLCore::HttpRequest; - mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); - mHttpOptions->setTransferTimeout(mMeshUploadTimeOut); - mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); - mHttpOptions->setRetries(UPLOAD_RETRY_LIMIT); - mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); - mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); - mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS); + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + mHttpOptions->setTransferTimeout(mMeshUploadTimeOut); + mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); + mHttpOptions->setRetries(UPLOAD_RETRY_LIMIT); + mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); + mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS); } LLMeshUploadThread::~LLMeshUploadThread() { - delete mHttpRequest; - mHttpRequest = NULL; - delete mMutex; - mMutex = NULL; + delete mHttpRequest; + mHttpRequest = NULL; + delete mMutex; + mMutex = NULL; } LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread) { - mStage = "single_hull"; - mModel = mdl; - mDecompID = &mdl->mDecompID; - mBaseModel = base_model; - mThread = thread; - - //copy out positions and indices - assignData(mdl) ; + mStage = "single_hull"; + mModel = mdl; + mDecompID = &mdl->mDecompID; + mBaseModel = base_model; + mThread = thread; + + //copy out positions and indices + assignData(mdl) ; - mThread->mFinalDecomp = this; - mThread->mPhysicsComplete = false; + mThread->mFinalDecomp = this; + mThread->mPhysicsComplete = false; } void LLMeshUploadThread::DecompRequest::completed() { - if (mThread->mFinalDecomp == this) - { - mThread->mPhysicsComplete = true; - } + if (mThread->mFinalDecomp == this) + { + mThread->mPhysicsComplete = true; + } + + llassert(mHull.size() == 1); - llassert(mHull.size() == 1); - - mThread->mHullMap[mBaseModel] = mHull[0]; + mThread->mHullMap[mBaseModel] = mHull[0]; } //called in the main thread. void LLMeshUploadThread::preStart() { - //build map of LLModel refs to instances for callbacks - for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter) - { - mInstance[iter->mModel].push_back(*iter); - } + //build map of LLModel refs to instances for callbacks + for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter) + { + mInstance[iter->mModel].push_back(*iter); + } } void LLMeshUploadThread::discard() { - LLMutexLock lock(mMutex); - mDiscarded = true; + LLMutexLock lock(mMutex); + mDiscarded = true; } bool LLMeshUploadThread::isDiscarded() const { - LLMutexLock lock(mMutex); - return mDiscarded; + LLMutexLock lock(mMutex); + return mDiscarded; } void LLMeshUploadThread::run() { - if (mDoUpload) - { - doWholeModelUpload(); - } - else - { - requestWholeModelFee(); - } + if (mDoUpload) + { + doWholeModelUpload(); + } + else + { + requestWholeModelFee(); + } } void dump_llsd_to_file(const LLSD& content, std::string filename) { - if (gSavedSettings.getBOOL("MeshUploadLogXML")) - { - llofstream of(filename.c_str()); - LLSDSerialize::toPrettyXML(content,of); - } + if (gSavedSettings.getBOOL("MeshUploadLogXML")) + { + llofstream of(filename.c_str()); + LLSDSerialize::toPrettyXML(content,of); + } } LLSD llsd_from_file(std::string filename) { - llifstream ifs(filename.c_str()); - LLSD result; - LLSDSerialize::fromXML(result,ifs); - return result; + llifstream ifs(filename.c_str()); + LLSD result; + LLSDSerialize::fromXML(result,ifs); + return result; } void LLMeshUploadThread::wholeModelToLLSD(LLSD& 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")); - - res["mesh_list"] = LLSD::emptyArray(); - res["texture_list"] = LLSD::emptyArray(); - res["instance_list"] = LLSD::emptyArray(); - S32 mesh_num = 0; - S32 texture_num = 0; - - std::set<LLViewerTexture* > textures; - std::map<LLViewerTexture*,S32> texture_index; - - std::map<LLModel*,S32> mesh_index; - std::string model_name; - - S32 instance_num = 0; - - for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) - { - LLMeshUploadData data; - data.mBaseModel = iter->first; - - if (data.mBaseModel->mSubmodelID) - { - // These are handled below to insure correct parenting order on creation - // due to map walking being based on model address (aka random) - continue; - } - - LLModelInstance& first_instance = *(iter->second.begin()); - for (S32 i = 0; i < 5; i++) - { - data.mModel[i] = first_instance.mLOD[i]; - } - - 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(); - } - - 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, + 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")); + + res["mesh_list"] = LLSD::emptyArray(); + res["texture_list"] = LLSD::emptyArray(); + res["instance_list"] = LLSD::emptyArray(); + S32 mesh_num = 0; + S32 texture_num = 0; + + std::set<LLViewerTexture* > textures; + std::map<LLViewerTexture*,S32> texture_index; + + std::map<LLModel*,S32> mesh_index; + std::string model_name; + + S32 instance_num = 0; + + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) + { + LLMeshUploadData data; + data.mBaseModel = iter->first; + + if (data.mBaseModel->mSubmodelID) + { + // These are handled below to insure correct parenting order on creation + // due to map walking being based on model address (aka random) + continue; + } + + LLModelInstance& first_instance = *(iter->second.begin()); + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = first_instance.mLOD[i]; + } + + 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(); + } + + 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++; - } - - // For all instances that use this model - for (instance_list::iterator instance_iter = iter->second.begin(); - instance_iter != iter->second.end(); - ++instance_iter) - { - - 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"] = 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; - - 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()) - { - 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++; - } - } - - for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) - { - LLMeshUploadData data; - data.mBaseModel = iter->first; - - if (!data.mBaseModel->mSubmodelID) - { - // These were handled above already... - // - continue; - } - - LLModelInstance& first_instance = *(iter->second.begin()); - for (S32 i = 0; i < 5; i++) - { - data.mModel[i] = first_instance.mLOD[i]; - } - - 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(); - } - - 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, + 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++; + } + + // For all instances that use this model + for (instance_list::iterator instance_iter = iter->second.begin(); + instance_iter != iter->second.end(); + ++instance_iter) + { + + 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"] = 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; + + 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()) + { + 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++; + } + } + + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) + { + LLMeshUploadData data; + data.mBaseModel = iter->first; + + if (!data.mBaseModel->mSubmodelID) + { + // These were handled above already... + // + continue; + } + + LLModelInstance& first_instance = *(iter->second.begin()); + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = first_instance.mLOD[i]; + } + + 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(); + } + + 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++; - } - - // For all instances that use this model - for (instance_list::iterator instance_iter = iter->second.begin(); - instance_iter != iter->second.end(); - ++instance_iter) - { - - 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()) - { - 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++; - } - } - - if (model_name.empty()) model_name = "mesh model"; - result["name"] = model_name; - res["metric"] = "MUT_Unspecified"; - result["asset_resources"] = res; - dump_llsd_to_file(result,make_dump_name("whole_model_",dump_num)); - - dest = result; + 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++; + } + + // For all instances that use this model + for (instance_list::iterator instance_iter = iter->second.begin(); + instance_iter != iter->second.end(); + ++instance_iter) + { + + 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()) + { + 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++; + } + } + + if (model_name.empty()) model_name = "mesh model"; + result["name"] = model_name; + res["metric"] = "MUT_Unspecified"; + result["asset_resources"] = res; + dump_llsd_to_file(result,make_dump_name("whole_model_",dump_num)); + + dest = result; } void LLMeshUploadThread::generateHulls() { - bool has_valid_requests = false ; - - for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) - { - LLMeshUploadData data; - data.mBaseModel = iter->first; - - LLModelInstance& instance = *(iter->second.begin()); - - for (S32 i = 0; i < 5; i++) - { - data.mModel[i] = instance.mLOD[i]; - } - - //queue up models for hull generation - LLModel* physics = NULL; - - if (data.mModel[LLModel::LOD_PHYSICS].notNull()) - { - physics = data.mModel[LLModel::LOD_PHYSICS]; - } - else if (data.mModel[LLModel::LOD_LOW].notNull()) - { - physics = data.mModel[LLModel::LOD_LOW]; - } - else if (data.mModel[LLModel::LOD_MEDIUM].notNull()) - { - physics = data.mModel[LLModel::LOD_MEDIUM]; - } - else - { - physics = data.mModel[LLModel::LOD_HIGH]; - } - - llassert(physics != NULL); - - DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this); - if(request->isValid()) - { - gMeshRepo.mDecompThread->submitRequest(request); - has_valid_requests = true ; - } - } - - if (has_valid_requests) - { - // *NOTE: Interesting livelock condition on shutdown. If there - // is an upload request in generateHulls() when shutdown starts, - // the main thread isn't available to manage communication between - // the decomposition thread and the upload thread and this loop - // wouldn't complete in turn stalling the main thread. The check - // on isDiscarded() prevents that. - while (! mPhysicsComplete && ! isDiscarded()) - { - apr_sleep(100); - } - } + bool has_valid_requests = false ; + + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) + { + LLMeshUploadData data; + data.mBaseModel = iter->first; + + LLModelInstance& instance = *(iter->second.begin()); + + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = instance.mLOD[i]; + } + + //queue up models for hull generation + LLModel* physics = NULL; + + if (data.mModel[LLModel::LOD_PHYSICS].notNull()) + { + physics = data.mModel[LLModel::LOD_PHYSICS]; + } + else if (data.mModel[LLModel::LOD_LOW].notNull()) + { + physics = data.mModel[LLModel::LOD_LOW]; + } + else if (data.mModel[LLModel::LOD_MEDIUM].notNull()) + { + physics = data.mModel[LLModel::LOD_MEDIUM]; + } + else + { + physics = data.mModel[LLModel::LOD_HIGH]; + } + + llassert(physics != NULL); + + DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this); + if(request->isValid()) + { + gMeshRepo.mDecompThread->submitRequest(request); + has_valid_requests = true ; + } + } + + if (has_valid_requests) + { + // *NOTE: Interesting livelock condition on shutdown. If there + // is an upload request in generateHulls() when shutdown starts, + // the main thread isn't available to manage communication between + // the decomposition thread and the upload thread and this loop + // wouldn't complete in turn stalling the main thread. The check + // on isDiscarded() prevents that. + while (! mPhysicsComplete && ! isDiscarded()) + { + apr_sleep(100); + } + } } void LLMeshUploadThread::doWholeModelUpload() { - LL_DEBUGS(LOG_MESH) << "Starting model upload. Instances: " << mInstance.size() << LL_ENDL; - - if (mWholeModelUploadURL.empty()) - { - LL_WARNS(LOG_MESH) << "Missing mesh upload capability, unable to upload, fee request failed." - << LL_ENDL; - } - else - { - generateHulls(); - LL_DEBUGS(LOG_MESH) << "Hull generation completed." << LL_ENDL; - - mModelData = LLSD::emptyMap(); - wholeModelToLLSD(mModelData, true); - LLSD body = mModelData["asset_resources"]; - - dump_llsd_to_file(body, make_dump_name("whole_model_body_", dump_num)); - - LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, - mHttpPolicyClass, - mWholeModelUploadURL, - body, - mHttpOptions, - mHttpHeaders, + LL_DEBUGS(LOG_MESH) << "Starting model upload. Instances: " << mInstance.size() << LL_ENDL; + + if (mWholeModelUploadURL.empty()) + { + LL_WARNS(LOG_MESH) << "Missing mesh upload capability, unable to upload, fee request failed." + << LL_ENDL; + } + else + { + generateHulls(); + LL_DEBUGS(LOG_MESH) << "Hull generation completed." << LL_ENDL; + + mModelData = LLSD::emptyMap(); + wholeModelToLLSD(mModelData, true); + LLSD body = mModelData["asset_resources"]; + + dump_llsd_to_file(body, make_dump_name("whole_model_body_", dump_num)); + + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, + mHttpPolicyClass, + mWholeModelUploadURL, + body, + mHttpOptions, + mHttpHeaders, LLCore::HttpHandler::ptr_t(this, &NoOpDeletor)); - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - mHttpStatus = mHttpRequest->getStatus(); - - LL_WARNS(LOG_MESH) << "Couldn't issue request for full model upload. Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toTerseString() << ")" - << LL_ENDL; - } - else - { - U32 sleep_time(10); - - LL_DEBUGS(LOG_MESH) << "POST request issued." << LL_ENDL; - - mHttpRequest->update(0); - while (! LLApp::isExiting() && ! finished() && ! isDiscarded()) - { - ms_sleep(sleep_time); - sleep_time = llmin(250U, sleep_time + sleep_time); - mHttpRequest->update(0); - } - - if (isDiscarded()) - { - LL_DEBUGS(LOG_MESH) << "Mesh upload operation discarded." << LL_ENDL; - } - else - { - LL_DEBUGS(LOG_MESH) << "Mesh upload operation completed." << LL_ENDL; - } - } - } + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + mHttpStatus = mHttpRequest->getStatus(); + + LL_WARNS(LOG_MESH) << "Couldn't issue request for full model upload. Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toTerseString() << ")" + << LL_ENDL; + } + else + { + U32 sleep_time(10); + + LL_DEBUGS(LOG_MESH) << "POST request issued." << LL_ENDL; + + mHttpRequest->update(0); + while (! LLApp::isExiting() && ! finished() && ! isDiscarded()) + { + ms_sleep(sleep_time); + sleep_time = llmin(250U, sleep_time + sleep_time); + mHttpRequest->update(0); + } + + if (isDiscarded()) + { + LL_DEBUGS(LOG_MESH) << "Mesh upload operation discarded." << LL_ENDL; + } + else + { + LL_DEBUGS(LOG_MESH) << "Mesh upload operation completed." << LL_ENDL; + } + } + } } void LLMeshUploadThread::requestWholeModelFee() { - dump_num++; + dump_num++; - generateHulls(); + generateHulls(); - mModelData = LLSD::emptyMap(); - wholeModelToLLSD(mModelData, false); - dump_llsd_to_file(mModelData, make_dump_name("whole_model_fee_request_", dump_num)); - LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, - mHttpPolicyClass, - mWholeModelFeeCapability, - mModelData, - mHttpOptions, - mHttpHeaders, + mModelData = LLSD::emptyMap(); + wholeModelToLLSD(mModelData, false); + dump_llsd_to_file(mModelData, make_dump_name("whole_model_fee_request_", dump_num)); + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, + mHttpPolicyClass, + mWholeModelFeeCapability, + mModelData, + mHttpOptions, + mHttpHeaders, LLCore::HttpHandler::ptr_t(this, &NoOpDeletor)); - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - mHttpStatus = mHttpRequest->getStatus(); - - LL_WARNS(LOG_MESH) << "Couldn't issue request for model fee. Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toTerseString() << ")" - << LL_ENDL; - } - else - { - U32 sleep_time(10); - - mHttpRequest->update(0); - while (! LLApp::isExiting() && ! finished() && ! isDiscarded()) - { - ms_sleep(sleep_time); - sleep_time = llmin(250U, sleep_time + sleep_time); - mHttpRequest->update(0); - } - if (isDiscarded()) - { - LL_DEBUGS(LOG_MESH) << "Mesh fee query operation discarded." << LL_ENDL; - } - } + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + mHttpStatus = mHttpRequest->getStatus(); + + LL_WARNS(LOG_MESH) << "Couldn't issue request for model fee. Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toTerseString() << ")" + << LL_ENDL; + } + else + { + U32 sleep_time(10); + + mHttpRequest->update(0); + while (! LLApp::isExiting() && ! finished() && ! isDiscarded()) + { + ms_sleep(sleep_time); + sleep_time = llmin(250U, sleep_time + sleep_time); + mHttpRequest->update(0); + } + if (isDiscarded()) + { + LL_DEBUGS(LOG_MESH) << "Mesh fee query operation discarded." << LL_ENDL; + } + } } // Does completion duty for both fee queries and actual uploads. void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { - // QA/Devel: 0x2 to enable fake error import on upload, 0x1 on fee check - const S32 fake_error(gSavedSettings.getS32("MeshUploadFakeErrors") & (mDoUpload ? 0xa : 0x5)); - LLCore::HttpStatus status(response->getStatus()); - if (fake_error) - { - status = (fake_error & 0x0c) ? LLCore::HttpStatus(500) : LLCore::HttpStatus(200); - } - std::string reason(status.toString()); - LLSD body; - - mFinished = true; - - if (mDoUpload) - { - // model upload case - LLWholeModelUploadObserver * observer(mUploadObserverHandle.get()); - - if (! status) - { - LL_WARNS(LOG_MESH) << "Upload failed. Reason: " << reason - << " (" << status.toTerseString() << ")" - << LL_ENDL; - - // Build a fake body for the alert generator - 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()); - - if (observer) - { - doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); - } - } - else - { - if (fake_error & 0x2) - { - body = llsd_from_file("fake_upload_error.xml"); - } - else - { - // *TODO: handle error in conversion process - LLCoreHttpUtil::responseToLLSD(response, true, body); - } - dump_llsd_to_file(body, make_dump_name("whole_model_upload_response_", dump_num)); - - if (body["state"].asString() == "complete") - { - // requested "mesh" asset type isn't actually the type - // of the resultant object, fix it up here. - mModelData["asset_type"] = "object"; - gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mModelData, body)); - - if (observer) - { - doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadSuccess, observer)); - } - } - else - { - LL_WARNS(LOG_MESH) << "Upload failed. Not in expected 'complete' state." << LL_ENDL; - log_upload_error(status, body, "upload", mModelData["name"].asString()); - - if (observer) - { - doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); - } - } - } - } - else - { - // model fee case - LLWholeModelFeeObserver* observer(mFeeObserverHandle.get()); - mWholeModelUploadURL.clear(); - - if (! status) - { - LL_WARNS(LOG_MESH) << "Fee request failed. Reason: " << reason - << " (" << status.toTerseString() << ")" - << LL_ENDL; - - // Build a fake body for the alert generator - 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()); - - if (observer) - { - observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason, body["error"]); - } - } - else - { - if (fake_error & 0x1) - { - body = llsd_from_file("fake_upload_error.xml"); - } - else - { - // *TODO: handle error in conversion process - LLCoreHttpUtil::responseToLLSD(response, true, body); - } - dump_llsd_to_file(body, make_dump_name("whole_model_fee_response_", dump_num)); - - if (body["state"].asString() == "upload") - { - mWholeModelUploadURL = body["uploader"].asString(); - - if (observer) - { - body["data"]["upload_price"] = body["upload_price"]; - observer->onModelPhysicsFeeReceived(body["data"], mWholeModelUploadURL); - } - } - else - { - LL_WARNS(LOG_MESH) << "Fee request failed. Not in expected 'upload' state." << LL_ENDL; - log_upload_error(status, body, "fee", mModelData["name"].asString()); - - if (observer) - { - observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason, body["error"]); - } - } - } - } + // QA/Devel: 0x2 to enable fake error import on upload, 0x1 on fee check + const S32 fake_error(gSavedSettings.getS32("MeshUploadFakeErrors") & (mDoUpload ? 0xa : 0x5)); + LLCore::HttpStatus status(response->getStatus()); + if (fake_error) + { + status = (fake_error & 0x0c) ? LLCore::HttpStatus(500) : LLCore::HttpStatus(200); + } + std::string reason(status.toString()); + LLSD body; + + mFinished = true; + + if (mDoUpload) + { + // model upload case + LLWholeModelUploadObserver * observer(mUploadObserverHandle.get()); + + if (! status) + { + LL_WARNS(LOG_MESH) << "Upload failed. Reason: " << reason + << " (" << status.toTerseString() << ")" + << LL_ENDL; + + // Build a fake body for the alert generator + 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()); + + if (observer) + { + doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); + } + } + else + { + if (fake_error & 0x2) + { + body = llsd_from_file("fake_upload_error.xml"); + } + else + { + // *TODO: handle error in conversion process + LLCoreHttpUtil::responseToLLSD(response, true, body); + } + dump_llsd_to_file(body, make_dump_name("whole_model_upload_response_", dump_num)); + + if (body["state"].asString() == "complete") + { + // requested "mesh" asset type isn't actually the type + // of the resultant object, fix it up here. + mModelData["asset_type"] = "object"; + gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mModelData, body)); + + if (observer) + { + doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadSuccess, observer)); + } + } + else + { + LL_WARNS(LOG_MESH) << "Upload failed. Not in expected 'complete' state." << LL_ENDL; + log_upload_error(status, body, "upload", mModelData["name"].asString()); + + if (observer) + { + doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); + } + } + } + } + else + { + // model fee case + LLWholeModelFeeObserver* observer(mFeeObserverHandle.get()); + mWholeModelUploadURL.clear(); + + if (! status) + { + LL_WARNS(LOG_MESH) << "Fee request failed. Reason: " << reason + << " (" << status.toTerseString() << ")" + << LL_ENDL; + + // Build a fake body for the alert generator + 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()); + + if (observer) + { + observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason, body["error"]); + } + } + else + { + if (fake_error & 0x1) + { + body = llsd_from_file("fake_upload_error.xml"); + } + else + { + // *TODO: handle error in conversion process + LLCoreHttpUtil::responseToLLSD(response, true, body); + } + dump_llsd_to_file(body, make_dump_name("whole_model_fee_response_", dump_num)); + + if (body["state"].asString() == "upload") + { + mWholeModelUploadURL = body["uploader"].asString(); + + if (observer) + { + body["data"]["upload_price"] = body["upload_price"]; + observer->onModelPhysicsFeeReceived(body["data"], mWholeModelUploadURL); + } + } + else + { + LL_WARNS(LOG_MESH) << "Fee request failed. Not in expected 'upload' state." << LL_ENDL; + log_upload_error(status, body, "fee", mModelData["name"].asString()); + + if (observer) + { + observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason, body["error"]); + } + } + } + } } void LLMeshRepoThread::notifyLoadedMeshes() { - bool update_metrics(false); - - if (!mMutex) - { - return; - } - - if (!mLoadedQ.empty()) - { - std::deque<LoadedMesh> loaded_queue; - - mMutex->lock(); - if (!mLoadedQ.empty()) - { - loaded_queue.swap(mLoadedQ); - mMutex->unlock(); - - update_metrics = true; - - // Process the elements free of the lock - for (const auto& mesh : loaded_queue) - { - if (mesh.mVolume->getNumVolumeFaces() > 0) - { - gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); - } - else - { - gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, - LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); - } - } - } - } - - if (!mUnavailableQ.empty()) - { - std::deque<LODRequest> unavil_queue; - - mMutex->lock(); - if (!mUnavailableQ.empty()) - { - unavil_queue.swap(mUnavailableQ); - mMutex->unlock(); - - update_metrics = true; - - // Process the elements free of the lock - for (const auto& req : unavil_queue) - { - gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); - } - } - } - - if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty()) - { - if (mMutex->trylock()) - { - std::deque<LLMeshSkinInfo*> skin_info_q; - std::deque<UUIDBasedRequest> skin_info_unavail_q; - std::list<LLModel::Decomposition*> decomp_q; - - if (! mSkinInfoQ.empty()) - { - skin_info_q.swap(mSkinInfoQ); - } - - if (! mSkinUnavailableQ.empty()) - { - skin_info_unavail_q.swap(mSkinUnavailableQ); - } - - if (! mDecompositionQ.empty()) - { - decomp_q.swap(mDecompositionQ); - } - - mMutex->unlock(); - - // Process the elements free of the lock - while (! skin_info_q.empty()) - { - gMeshRepo.notifySkinInfoReceived(skin_info_q.front()); - skin_info_q.pop_front(); - } - while (! skin_info_unavail_q.empty()) - { - gMeshRepo.notifySkinInfoUnavailable(skin_info_unavail_q.front().mId); - skin_info_unavail_q.pop_front(); - } - - while (! decomp_q.empty()) - { - gMeshRepo.notifyDecompositionReceived(decomp_q.front()); - decomp_q.pop_front(); - } - } - } - - if (update_metrics) - { - // Ping time-to-load metrics for mesh download operations. - LLMeshRepository::metricsProgress(0); - } - -} - -S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) + bool update_metrics(false); + + if (!mMutex) + { + return; + } + + if (!mLoadedQ.empty()) + { + std::deque<LoadedMesh> loaded_queue; + + mMutex->lock(); + if (!mLoadedQ.empty()) + { + loaded_queue.swap(mLoadedQ); + mMutex->unlock(); + + update_metrics = true; + + // Process the elements free of the lock + for (const auto& mesh : loaded_queue) + { + if (mesh.mVolume->getNumVolumeFaces() > 0) + { + gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); + } + else + { + gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, + LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); + } + } + } + } + + if (!mUnavailableQ.empty()) + { + std::deque<LODRequest> unavil_queue; + + mMutex->lock(); + if (!mUnavailableQ.empty()) + { + unavil_queue.swap(mUnavailableQ); + mMutex->unlock(); + + update_metrics = true; + + // Process the elements free of the lock + for (const auto& req : unavil_queue) + { + gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); + } + } + } + + if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty()) + { + if (mMutex->trylock()) + { + std::deque<LLMeshSkinInfo*> skin_info_q; + std::deque<UUIDBasedRequest> skin_info_unavail_q; + std::list<LLModel::Decomposition*> decomp_q; + + if (! mSkinInfoQ.empty()) + { + skin_info_q.swap(mSkinInfoQ); + } + + if (! mSkinUnavailableQ.empty()) + { + skin_info_unavail_q.swap(mSkinUnavailableQ); + } + + if (! mDecompositionQ.empty()) + { + decomp_q.swap(mDecompositionQ); + } + + mMutex->unlock(); + + // Process the elements free of the lock + while (! skin_info_q.empty()) + { + gMeshRepo.notifySkinInfoReceived(skin_info_q.front()); + skin_info_q.pop_front(); + } + while (! skin_info_unavail_q.empty()) + { + gMeshRepo.notifySkinInfoUnavailable(skin_info_unavail_q.front().mId); + skin_info_unavail_q.pop_front(); + } + + while (! decomp_q.empty()) + { + gMeshRepo.notifyDecompositionReceived(decomp_q.front()); + decomp_q.pop_front(); + } + } + } + + if (update_metrics) + { + // Ping time-to-load metrics for mesh download operations. + LLMeshRepository::metricsProgress(0); + } + +} + +S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { //only ever called from main thread - LLMutexLock lock(mHeaderMutex); - mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID()); + LLMutexLock lock(mHeaderMutex); + mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID()); - if (iter != mMeshHeader.end()) - { - auto& header = iter->second.second; + if (iter != mMeshHeader.end()) + { + auto& header = iter->second.second; - return LLMeshRepository::getActualMeshLOD(header, lod); - } + return LLMeshRepository::getActualMeshLOD(header, lod); + } - return lod; + return lod; } //static S32 LLMeshRepository::getActualMeshLOD(LLMeshHeader& header, S32 lod) { - lod = llclamp(lod, 0, 3); - - if (header.m404) - { - return -1; - } - - S32 version = header.mVersion; - - if (version > MAX_MESH_VERSION) - { - return -1; - } - - if (header.mLodSize[lod] > 0) - { - return lod; - } - - //search down to find the next available lower lod - for (S32 i = lod-1; i >= 0; --i) - { - if (header.mLodSize[i] > 0) - { - return i; - } - } - - //search up to find then ext available higher lod - for (S32 i = lod+1; i < LLVolumeLODGroup::NUM_LODS; ++i) - { - if (header.mLodSize[i] > 0) - { - return i; - } - } - - //header exists and no good lod found, treat as 404 + lod = llclamp(lod, 0, 3); + + if (header.m404) + { + return -1; + } + + S32 version = header.mVersion; + + if (version > MAX_MESH_VERSION) + { + return -1; + } + + if (header.mLodSize[lod] > 0) + { + return lod; + } + + //search down to find the next available lower lod + for (S32 i = lod-1; i >= 0; --i) + { + if (header.mLodSize[i] > 0) + { + return i; + } + } + + //search up to find then ext available higher lod + for (S32 i = lod+1; i < LLVolumeLODGroup::NUM_LODS; ++i) + { + if (header.mLodSize[i] > 0) + { + return i; + } + } + + //header exists and no good lod found, treat as 404 header.m404 = true; return -1; @@ -3049,335 +3049,335 @@ S32 LLMeshRepository::getActualMeshLOD(LLMeshHeader& header, S32 lod) // are cases far off the norm. void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { - mProcessed = true; - - unsigned int retries(0U); - response->getRetries(NULL, &retries); - LLMeshRepository::sHTTPRetryCount += retries; - - LLCore::HttpStatus status(response->getStatus()); - if (! status || MESH_HTTP_RESPONSE_FAILED) - { - processFailure(status); - ++LLMeshRepository::sHTTPErrorCount; - } - else - { - // From texture fetch code and may apply here: - // - // A warning about partial (HTTP 206) data. Some grid services - // do *not* return a 'Content-Range' header in the response to - // Range requests with a 206 status. We're forced to assume - // we get what we asked for in these cases until we can fix - // the services. - // - // May also need to deal with 200 status (full asset returned - // rather than partial) and 416 (request completely unsatisfyable). - // Always been exposed to these but are less likely here where - // speculative loads aren't done. - LLCore::BufferArray * body(response->getBody()); - S32 body_offset(0); - U8 * data(NULL); - S32 data_size(body ? body->size() : 0); - - if (data_size > 0) - { - static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); - - unsigned int offset(0), length(0), full_length(0); - - if (par_status == status) - { - // 206 case - response->getRange(&offset, &length, &full_length); - if (! offset && ! length) - { - // This is the case where we receive a 206 status but - // there wasn't a useful Content-Range header in the response. - // This could be because it was badly formatted but is more - // likely due to capabilities services which scrub headers - // from responses. Assume we got what we asked for...` - // length = data_size; - offset = mOffset; - } - } - else - { - // 200 case, typically - offset = 0; - } - - // *DEBUG: To test validation below - // offset += 1; - - // Validate that what we think we received is consistent with - // what we've asked for. I.e. first byte we wanted lies somewhere - // in the response. - if (offset > mOffset - || (offset + data_size) <= mOffset - || (mOffset - offset) >= data_size) - { - // No overlap with requested range. Fail request with - // suitable error. Shouldn't happen unless server/cache/ISP - // is doing something awful. - LL_WARNS(LOG_MESH) << "Mesh response (bytes [" - << offset << ".." << (offset + length - 1) - << "]) didn't overlap with request's origin (bytes [" - << mOffset << ".." << (mOffset + mRequestedBytes - 1) - << "])." << LL_ENDL; - processFailure(LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_INV_CONTENT_RANGE_HDR)); - ++LLMeshRepository::sHTTPErrorCount; - goto common_exit; - } - - // *TODO: Try to get rid of data copying and add interfaces - // that support BufferArray directly. Introduce a two-phase - // handler, optional first that takes a body, fallback second - // that requires a temporary allocation and data copy. - body_offset = mOffset - offset; - data = new(std::nothrow) U8[data_size - body_offset]; - if (data) - { - body->read(body_offset, (char *) data, data_size - body_offset); - LLMeshRepository::sBytesReceived += data_size; - } - else - { - LL_WARNS(LOG_MESH) << "Failed to allocate " << data_size - body_offset << " memory for mesh response" << LL_ENDL; - processFailure(LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_BAD_ALLOC)); - } - } - - processData(body, body_offset, data, data_size - body_offset); - - delete [] data; - } - - // Release handler + mProcessed = true; + + unsigned int retries(0U); + response->getRetries(NULL, &retries); + LLMeshRepository::sHTTPRetryCount += retries; + + LLCore::HttpStatus status(response->getStatus()); + if (! status || MESH_HTTP_RESPONSE_FAILED) + { + processFailure(status); + ++LLMeshRepository::sHTTPErrorCount; + } + else + { + // From texture fetch code and may apply here: + // + // A warning about partial (HTTP 206) data. Some grid services + // do *not* return a 'Content-Range' header in the response to + // Range requests with a 206 status. We're forced to assume + // we get what we asked for in these cases until we can fix + // the services. + // + // May also need to deal with 200 status (full asset returned + // rather than partial) and 416 (request completely unsatisfyable). + // Always been exposed to these but are less likely here where + // speculative loads aren't done. + LLCore::BufferArray * body(response->getBody()); + S32 body_offset(0); + U8 * data(NULL); + S32 data_size(body ? body->size() : 0); + + if (data_size > 0) + { + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + unsigned int offset(0), length(0), full_length(0); + + if (par_status == status) + { + // 206 case + response->getRange(&offset, &length, &full_length); + if (! offset && ! length) + { + // This is the case where we receive a 206 status but + // there wasn't a useful Content-Range header in the response. + // This could be because it was badly formatted but is more + // likely due to capabilities services which scrub headers + // from responses. Assume we got what we asked for...` + // length = data_size; + offset = mOffset; + } + } + else + { + // 200 case, typically + offset = 0; + } + + // *DEBUG: To test validation below + // offset += 1; + + // Validate that what we think we received is consistent with + // what we've asked for. I.e. first byte we wanted lies somewhere + // in the response. + if (offset > mOffset + || (offset + data_size) <= mOffset + || (mOffset - offset) >= data_size) + { + // No overlap with requested range. Fail request with + // suitable error. Shouldn't happen unless server/cache/ISP + // is doing something awful. + LL_WARNS(LOG_MESH) << "Mesh response (bytes [" + << offset << ".." << (offset + length - 1) + << "]) didn't overlap with request's origin (bytes [" + << mOffset << ".." << (mOffset + mRequestedBytes - 1) + << "])." << LL_ENDL; + processFailure(LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_INV_CONTENT_RANGE_HDR)); + ++LLMeshRepository::sHTTPErrorCount; + goto common_exit; + } + + // *TODO: Try to get rid of data copying and add interfaces + // that support BufferArray directly. Introduce a two-phase + // handler, optional first that takes a body, fallback second + // that requires a temporary allocation and data copy. + body_offset = mOffset - offset; + data = new(std::nothrow) U8[data_size - body_offset]; + if (data) + { + body->read(body_offset, (char *) data, data_size - body_offset); + LLMeshRepository::sBytesReceived += data_size; + } + else + { + LL_WARNS(LOG_MESH) << "Failed to allocate " << data_size - body_offset << " memory for mesh response" << LL_ENDL; + processFailure(LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_BAD_ALLOC)); + } + } + + processData(body, body_offset, data, data_size - body_offset); + + delete [] data; + } + + // Release handler common_exit: - gMeshRepo.mThread->mHttpRequestSet.erase(this->shared_from_this()); + gMeshRepo.mThread->mHttpRequestSet.erase(this->shared_from_this()); } LLMeshHeaderHandler::~LLMeshHeaderHandler() { - if (!LLApp::isExiting()) - { - if (! mProcessed) - { - // something went wrong, retry - LL_WARNS(LOG_MESH) << "Mesh header fetch canceled unexpectedly, retrying." << LL_ENDL; - LLMeshRepoThread::HeaderRequest req(mMeshParams); - LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mHeaderReqQ.push(req); - } - LLMeshRepoThread::decActiveHeaderRequests(); - } + if (!LLApp::isExiting()) + { + if (! mProcessed) + { + // something went wrong, retry + LL_WARNS(LOG_MESH) << "Mesh header fetch canceled unexpectedly, retrying." << LL_ENDL; + LLMeshRepoThread::HeaderRequest req(mMeshParams); + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mHeaderReqQ.push(req); + } + LLMeshRepoThread::decActiveHeaderRequests(); + } } void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) { - LL_WARNS(LOG_MESH) << "Error during mesh header handling. ID: " << mMeshParams.getSculptID() - << ", Reason: " << status.toString() - << " (" << status.toTerseString() << "). Not retrying." - << LL_ENDL; + LL_WARNS(LOG_MESH) << "Error during mesh header handling. ID: " << mMeshParams.getSculptID() + << ", Reason: " << status.toString() + << " (" << status.toTerseString() << "). Not retrying." + << LL_ENDL; - // Can't get the header so none of the LODs will be available - LLMutexLock lock(gMeshRepo.mThread->mMutex); - for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) - { - gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); - } + // Can't get the header so none of the LODs will be available + LLMutexLock lock(gMeshRepo.mThread->mMutex); + for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) + { + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); + } } void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, - U8 * data, S32 data_size) + U8 * data, S32 data_size) { - LLUUID mesh_id = mMeshParams.getSculptID(); + 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; - llassert(success); + llassert(success); EMeshProcessingResult res = MESH_UNKNOWN; if (success) { res = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); success = (res == MESH_OK); } - if (! success) - { - // *TODO: Get real reason for parse failure here. Might we want to retry? - LL_WARNS(LOG_MESH) << "Unable to parse mesh header. ID: " << mesh_id - << ", Size: " << data_size - << ", Reason: " << res << " Not retrying." - << LL_ENDL; - - // Can't get the header so none of the LODs will be available - LLMutexLock lock(gMeshRepo.mThread->mMutex); - for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) - { - gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); - } - } - else if (data && data_size > 0) - { - // header was successfully retrieved from sim and parsed and is in cache - S32 header_bytes = 0; - LLMeshHeader header; - - gMeshRepo.mThread->mHeaderMutex->lock(); - 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; - } - - if (header_bytes > 0 - && !header.m404 - && (header.mVersion <= MAX_MESH_VERSION)) - { - std::stringstream str; - - S32 lod_bytes = 0; - - for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) - { - // figure out how many bytes we'll need to reserve in the file - lod_bytes = llmax(lod_bytes, header.mLodOffset[i]+header.mLodSize[i]); - } - - // just in case skin info or decomposition is at the end of the file (which it shouldn't be) - lod_bytes = llmax(lod_bytes, header.mSkinOffset+header.mSkinSize); + if (! success) + { + // *TODO: Get real reason for parse failure here. Might we want to retry? + LL_WARNS(LOG_MESH) << "Unable to parse mesh header. ID: " << mesh_id + << ", Size: " << data_size + << ", Reason: " << res << " Not retrying." + << LL_ENDL; + + // Can't get the header so none of the LODs will be available + LLMutexLock lock(gMeshRepo.mThread->mMutex); + for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) + { + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); + } + } + else if (data && data_size > 0) + { + // header was successfully retrieved from sim and parsed and is in cache + S32 header_bytes = 0; + LLMeshHeader header; + + gMeshRepo.mThread->mHeaderMutex->lock(); + 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; + } + + if (header_bytes > 0 + && !header.m404 + && (header.mVersion <= MAX_MESH_VERSION)) + { + std::stringstream str; + + S32 lod_bytes = 0; + + for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) + { + // figure out how many bytes we'll need to reserve in the file + lod_bytes = llmax(lod_bytes, header.mLodOffset[i]+header.mLodSize[i]); + } + + // just in case skin info or decomposition is at the end of the file (which it shouldn't be) + lod_bytes = llmax(lod_bytes, header.mSkinOffset+header.mSkinSize); lod_bytes = llmax(lod_bytes, header.mPhysicsConvexOffset + header.mPhysicsConvexSize); // Do not unlock mutex untill we are done with LLSD. // LLSD is smart and can work like smart pointer, is not thread safe. gMeshRepo.mThread->mHeaderMutex->unlock(); - S32 bytes = lod_bytes + header_bytes; - - - // It's possible for the remote asset to have more data than is needed for the local cache - // only allocate as much space in the cache as is needed for the local cache - data_size = llmin(data_size, bytes); - - // <FS:Ansariel> Fix asset caching - //LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::WRITE); - LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - if (file.getMaxSize() >= bytes) - { - LLMeshRepository::sCacheBytesWritten += data_size; - ++LLMeshRepository::sCacheWrites; - - file.write(data, data_size); - - // <FS:Ansariel> Fix asset caching - S32 remaining = bytes - file.tell(); - if (remaining > 0) - { - U8* block = new(std::nothrow) U8[remaining]; - if (block) - { - memset(block, 0, remaining); - file.write(block, remaining); - delete[] block; - } - } - // </FS:Ansariel> - } - } - else - { - LL_WARNS(LOG_MESH) << "Trying to cache nonexistent mesh, mesh id: " << mesh_id << LL_ENDL; - - 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); - for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) - { - gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); - } - } - } + S32 bytes = lod_bytes + header_bytes; + + + // It's possible for the remote asset to have more data than is needed for the local cache + // only allocate as much space in the cache as is needed for the local cache + data_size = llmin(data_size, bytes); + + // <FS:Ansariel> Fix asset caching + //LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + if (file.getMaxSize() >= bytes) + { + LLMeshRepository::sCacheBytesWritten += data_size; + ++LLMeshRepository::sCacheWrites; + + file.write(data, data_size); + + // <FS:Ansariel> Fix asset caching + S32 remaining = bytes - file.tell(); + if (remaining > 0) + { + U8* block = new(std::nothrow) U8[remaining]; + if (block) + { + memset(block, 0, remaining); + file.write(block, remaining); + delete[] block; + } + } + // </FS:Ansariel> + } + } + else + { + LL_WARNS(LOG_MESH) << "Trying to cache nonexistent mesh, mesh id: " << mesh_id << LL_ENDL; + + 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); + for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) + { + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); + } + } + } } LLMeshLODHandler::~LLMeshLODHandler() { - if (! LLApp::isExiting()) - { - if (! mProcessed) - { - LL_WARNS(LOG_MESH) << "Mesh LOD fetch canceled unexpectedly, retrying." << LL_ENDL; - gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD); - } - LLMeshRepoThread::decActiveLODRequests(); - } + if (! LLApp::isExiting()) + { + if (! mProcessed) + { + LL_WARNS(LOG_MESH) << "Mesh LOD fetch canceled unexpectedly, retrying." << LL_ENDL; + gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD); + } + LLMeshRepoThread::decActiveLODRequests(); + } } void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) { - LL_WARNS(LOG_MESH) << "Error during mesh LOD handling. ID: " << mMeshParams.getSculptID() - << ", Reason: " << status.toString() - << " (" << status.toTerseString() << "). Not retrying." - << LL_ENDL; + LL_WARNS(LOG_MESH) << "Error during mesh LOD handling. ID: " << mMeshParams.getSculptID() + << ", Reason: " << status.toString() + << " (" << status.toTerseString() << "). Not retrying." + << LL_ENDL; - LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, - U8 * data, S32 data_size) -{ - 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) - { - // good fetch from sim, write to cache - // <FS:Ansariel> Fix asset caching - //LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::WRITE); - LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - - S32 offset = mOffset; - S32 size = mRequestedBytes; - - if (file.getSize() >= offset+size) - { - file.seek(offset); - 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->mMutex); - gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); - } - } - else - { - LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID() - << ", Unknown reason. Not retrying." - << " LOD: " << mLOD - << " Data size: " << data_size - << LL_ENDL; - LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); - } + U8 * data, S32 data_size) +{ + 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) + { + // good fetch from sim, write to cache + // <FS:Ansariel> Fix asset caching + //LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + file.seek(offset); + 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->mMutex); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); + } + } + else + { + LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID() + << ", Unknown reason. Not retrying." + << " LOD: " << mLOD + << " Data size: " << data_size + << LL_ENDL; + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); + } } LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() { - if (!mProcessed) + if (!mProcessed) { LL_WARNS(LOG_MESH) << "deleting unprocessed request handler (may be ok on exit)" << LL_ENDL; } @@ -3385,50 +3385,50 @@ LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) { - LL_WARNS(LOG_MESH) << "Error during mesh skin info handling. ID: " << mMeshID - << ", Reason: " << status.toString() - << " (" << status.toTerseString() << "). Not retrying." - << LL_ENDL; - LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); + LL_WARNS(LOG_MESH) << "Error during mesh skin info handling. ID: " << mMeshID + << ", Reason: " << status.toString() + << " (" << status.toTerseString() << "). Not retrying." + << LL_ENDL; + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); } void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, - U8 * data, S32 data_size) -{ - 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)) - { - // good fetch from sim, write to cache - // <FS:Ansariel> Fix asset caching - //LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); - LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - - S32 offset = mOffset; - S32 size = mRequestedBytes; - - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesWritten += size; - ++LLMeshRepository::sCacheWrites; - file.seek(offset); - 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->mMutex); - gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); - } + U8 * data, S32 data_size) +{ + 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)) + { + // good fetch from sim, write to cache + // <FS:Ansariel> Fix asset caching + //LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + ++LLMeshRepository::sCacheWrites; + file.seek(offset); + 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->mMutex); + gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); + } } LLMeshDecompositionHandler::~LLMeshDecompositionHandler() { - if (!mProcessed) + if (!mProcessed) { LL_WARNS(LOG_MESH) << "deleting unprocessed request handler (may be ok on exit)" << LL_ENDL; } @@ -3436,49 +3436,49 @@ LLMeshDecompositionHandler::~LLMeshDecompositionHandler() void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status) { - LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling. ID: " << mMeshID - << ", Reason: " << status.toString() - << " (" << status.toTerseString() << "). Not retrying." - << LL_ENDL; - // *TODO: Mark mesh unavailable on error. For now, simply leave - // request unfulfilled rather than retry forever. + LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling. ID: " << mMeshID + << ", Reason: " << status.toString() + << " (" << status.toTerseString() << "). Not retrying." + << LL_ENDL; + // *TODO: Mark mesh unavailable on error. For now, simply leave + // request unfulfilled rather than retry forever. } void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, - U8 * data, S32 data_size) -{ - if ((!MESH_DECOMP_PROCESS_FAILED) - && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong - && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) - { - // good fetch from sim, write to cache - // <FS:Ansariel> Fix asset caching - //LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); - LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - - S32 offset = mOffset; - S32 size = mRequestedBytes; - - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesWritten += size; - ++LLMeshRepository::sCacheWrites; - file.seek(offset); - file.write(data, size); - } - } - else - { - LL_WARNS(LOG_MESH) << "Error during mesh decomposition processing. ID: " << mMeshID - << ", Unknown reason. Not retrying." - << LL_ENDL; - // *TODO: Mark mesh unavailable on error - } + U8 * data, S32 data_size) +{ + if ((!MESH_DECOMP_PROCESS_FAILED) + && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong + && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) + { + // good fetch from sim, write to cache + // <FS:Ansariel> Fix asset caching + //LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + ++LLMeshRepository::sCacheWrites; + file.seek(offset); + file.write(data, size); + } + } + else + { + LL_WARNS(LOG_MESH) << "Error during mesh decomposition processing. ID: " << mMeshID + << ", Unknown reason. Not retrying." + << LL_ENDL; + // *TODO: Mark mesh unavailable on error + } } LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler() { - if (!mProcessed) + if (!mProcessed) { LL_WARNS(LOG_MESH) << "deleting unprocessed request handler (may be ok on exit)" << LL_ENDL; } @@ -3486,43 +3486,43 @@ LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler() void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status) { - LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling. ID: " << mMeshID - << ", Reason: " << status.toString() - << " (" << status.toTerseString() << "). Not retrying." - << LL_ENDL; - // *TODO: Mark mesh unavailable on error + LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling. ID: " << mMeshID + << ", Reason: " << status.toString() + << " (" << status.toTerseString() << "). Not retrying." + << LL_ENDL; + // *TODO: Mark mesh unavailable on error } void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, - U8 * data, S32 data_size) -{ - if ((!MESH_PHYS_SHAPE_PROCESS_FAILED) - && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong - && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size) == MESH_OK) - { - // good fetch from sim, write to cache for caching - // <FS:Ansariel> Fix asset caching - //LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); - LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - - S32 offset = mOffset; - S32 size = mRequestedBytes; - - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesWritten += size; - ++LLMeshRepository::sCacheWrites; - file.seek(offset); - file.write(data, size); - } - } - else - { - LL_WARNS(LOG_MESH) << "Error during mesh physics shape processing. ID: " << mMeshID - << ", Unknown reason. Not retrying." - << LL_ENDL; - // *TODO: Mark mesh unavailable on error - } + U8 * data, S32 data_size) +{ + if ((!MESH_PHYS_SHAPE_PROCESS_FAILED) + && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong + && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size) == MESH_OK) + { + // good fetch from sim, write to cache for caching + // <FS:Ansariel> Fix asset caching + //LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + ++LLMeshRepository::sCacheWrites; + file.seek(offset); + file.write(data, size); + } + } + else + { + LL_WARNS(LOG_MESH) << "Error during mesh physics shape processing. ID: " << mMeshID + << ", Unknown reason. Not retrying." + << LL_ENDL; + // *TODO: Mark mesh unavailable on error + } } LLMeshRepository::LLMeshRepository() @@ -3531,208 +3531,208 @@ LLMeshRepository::LLMeshRepository() mMeshThreadCount(0), mThread(NULL) { - mSkinInfoCullTimer.resetWithExpiry(10.f); + mSkinInfoCullTimer.resetWithExpiry(10.f); } void LLMeshRepository::init() { - mMeshMutex = new LLMutex(); - - LLConvexDecomposition::getInstance()->initSystem(); + mMeshMutex = new LLMutex(); + + LLConvexDecomposition::getInstance()->initSystem(); if (!LLConvexDecomposition::isFunctional()) { LL_INFOS(LOG_MESH) << "Using STUB for LLConvexDecomposition" << LL_ENDL; } - mDecompThread = new LLPhysicsDecomp(); - mDecompThread->start(); + mDecompThread = new LLPhysicsDecomp(); + mDecompThread->start(); + + while (!mDecompThread->mInited) + { //wait for physics decomp thread to init + apr_sleep(100); + } - while (!mDecompThread->mInited) - { //wait for physics decomp thread to init - apr_sleep(100); - } + metrics_teleport_started_signal = LLViewerMessage::getInstance()->setTeleportStartedCallback(teleport_started); - metrics_teleport_started_signal = LLViewerMessage::getInstance()->setTeleportStartedCallback(teleport_started); - - mThread = new LLMeshRepoThread(); - mThread->start(); + mThread = new LLMeshRepoThread(); + mThread->start(); } void LLMeshRepository::shutdown() { - LL_INFOS(LOG_MESH) << "Shutting down mesh repository." << LL_ENDL; - llassert(mThread != NULL); - llassert(mThread->mSignal != NULL); + LL_INFOS(LOG_MESH) << "Shutting down mesh repository." << LL_ENDL; + llassert(mThread != NULL); + llassert(mThread->mSignal != NULL); - metrics_teleport_started_signal.disconnect(); + metrics_teleport_started_signal.disconnect(); - for (U32 i = 0; i < mUploads.size(); ++i) - { - LL_INFOS(LOG_MESH) << "Discard the pending mesh uploads." << LL_ENDL; - mUploads[i]->discard() ; //discard the uploading requests. - } + for (U32 i = 0; i < mUploads.size(); ++i) + { + LL_INFOS(LOG_MESH) << "Discard the pending mesh uploads." << LL_ENDL; + mUploads[i]->discard() ; //discard the uploading requests. + } - mThread->mSignal->broadcast(); - - while (!mThread->isStopped()) - { - apr_sleep(10); - } - delete mThread; - mThread = NULL; + mThread->mSignal->broadcast(); - for (U32 i = 0; i < mUploads.size(); ++i) - { - LL_INFOS(LOG_MESH) << "Waiting for pending mesh upload " << (i + 1) << "/" << mUploads.size() << LL_ENDL; - while (!mUploads[i]->isStopped()) - { - apr_sleep(10); - } - delete mUploads[i]; - } + while (!mThread->isStopped()) + { + apr_sleep(10); + } + delete mThread; + mThread = NULL; - mUploads.clear(); + for (U32 i = 0; i < mUploads.size(); ++i) + { + LL_INFOS(LOG_MESH) << "Waiting for pending mesh upload " << (i + 1) << "/" << mUploads.size() << LL_ENDL; + while (!mUploads[i]->isStopped()) + { + apr_sleep(10); + } + delete mUploads[i]; + } - delete mMeshMutex; - mMeshMutex = NULL; + mUploads.clear(); - LL_INFOS(LOG_MESH) << "Shutting down decomposition system." << LL_ENDL; + delete mMeshMutex; + mMeshMutex = NULL; - if (mDecompThread) - { - mDecompThread->shutdown(); - delete mDecompThread; - mDecompThread = NULL; - } + LL_INFOS(LOG_MESH) << "Shutting down decomposition system." << LL_ENDL; + + if (mDecompThread) + { + mDecompThread->shutdown(); + delete mDecompThread; + mDecompThread = NULL; + } - LLConvexDecomposition::quitSystem(); + LLConvexDecomposition::quitSystem(); } //called in the main thread. S32 LLMeshRepository::update() { - // Conditionally log a mesh metrics event - metricsUpdate(); - - if(mUploadWaitList.empty()) - { - return 0 ; - } + // Conditionally log a mesh metrics event + metricsUpdate(); - S32 size = mUploadWaitList.size() ; - for (S32 i = 0; i < size; ++i) - { - mUploads.push_back(mUploadWaitList[i]); - mUploadWaitList[i]->preStart() ; - mUploadWaitList[i]->start() ; - } - mUploadWaitList.clear() ; + if(mUploadWaitList.empty()) + { + return 0 ; + } + + S32 size = mUploadWaitList.size() ; + for (S32 i = 0; i < size; ++i) + { + mUploads.push_back(mUploadWaitList[i]); + mUploadWaitList[i]->preStart() ; + mUploadWaitList[i]->start() ; + } + mUploadWaitList.clear() ; - return size ; + return size ; } void LLMeshRepository::unregisterMesh(LLVOVolume* vobj) { - for (auto& lod : mLoadingMeshes) - { - for (auto& param : lod) - { - vector_replace_with_last(param.second, vobj); - } - } + for (auto& lod : mLoadingMeshes) + { + for (auto& param : lod) + { + vector_replace_with_last(param.second, vobj); + } + } - for (auto& skin_pair : mLoadingSkins) - { - vector_replace_with_last(skin_pair.second, vobj); - } + for (auto& skin_pair : mLoadingSkins) + { + vector_replace_with_last(skin_pair.second, vobj); + } } S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, 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) - { - return detail; - } - - { - 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()) - { //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); - } - } - else - { - //first request for this mesh - mLoadingMeshes[detail][mesh_id].push_back(vobj); - mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail)); - LLMeshRepository::sLODPending++; - } - } - - //do a quick search to see if we can't display something while we wait for this mesh to load - LLVolume* volume = vobj->getVolume(); - - if (volume) - { - LLVolumeParams params = volume->getParams(); - - LLVolumeLODGroup* group = LLPrimitive::getVolumeManager()->getGroup(params); - - if (group) - { - //first, see if last_lod is available (don't transition down to avoid funny popping a la SH-641) - if (last_lod >= 0) - { - LLVolume* lod = group->refLOD(last_lod); - if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) - { - group->derefLOD(lod); - return last_lod; - } - group->derefLOD(lod); - } - - //next, see what the next lowest LOD available might be - for (S32 i = detail-1; i >= 0; --i) - { - LLVolume* lod = group->refLOD(i); - if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) - { - group->derefLOD(lod); - return i; - } - - group->derefLOD(lod); - } - - //no lower LOD is a available, is a higher lod available? - for (S32 i = detail+1; i < LLVolumeLODGroup::NUM_LODS; ++i) - { - LLVolume* lod = group->refLOD(i); - if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) - { - group->derefLOD(lod); - return i; - } - - group->derefLOD(lod); - } - } - } - - return detail; + + // Manage time-to-load metrics for mesh download operations. + metricsProgress(1); + + if (detail < 0 || detail >= LLVolumeLODGroup::NUM_LODS) + { + return detail; + } + + { + 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()) + { //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); + } + } + else + { + //first request for this mesh + mLoadingMeshes[detail][mesh_id].push_back(vobj); + mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail)); + LLMeshRepository::sLODPending++; + } + } + + //do a quick search to see if we can't display something while we wait for this mesh to load + LLVolume* volume = vobj->getVolume(); + + if (volume) + { + LLVolumeParams params = volume->getParams(); + + LLVolumeLODGroup* group = LLPrimitive::getVolumeManager()->getGroup(params); + + if (group) + { + //first, see if last_lod is available (don't transition down to avoid funny popping a la SH-641) + if (last_lod >= 0) + { + LLVolume* lod = group->refLOD(last_lod); + if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) + { + group->derefLOD(lod); + return last_lod; + } + group->derefLOD(lod); + } + + //next, see what the next lowest LOD available might be + for (S32 i = detail-1; i >= 0; --i) + { + LLVolume* lod = group->refLOD(i); + if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) + { + group->derefLOD(lod); + return i; + } + + group->derefLOD(lod); + } + + //no lower LOD is a available, is a higher lod available? + for (S32 i = detail+1; i < LLVolumeLODGroup::NUM_LODS; ++i) + { + LLVolume* lod = group->refLOD(i); + if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) + { + group->derefLOD(lod); + return i; + } + + group->derefLOD(lod); + } + } + } + + return detail; } void LLMeshRepository::notifyLoadedMeshes() @@ -3754,353 +3754,353 @@ void LLMeshRepository::notifyLoadedMeshes() LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, REQUEST2_LOW_WATER_MIN, REQUEST2_LOW_WATER_MAX); - - //clean up completed upload threads - for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); ) - { - LLMeshUploadThread* thread = *iter; - - if (thread->isStopped() && thread->finished()) - { - iter = mUploads.erase(iter); - delete thread; - } - else - { - ++iter; - } - } - - //update inventory - if (!mInventoryQ.empty()) - { - LLMutexLock lock(mMeshMutex); - while (!mInventoryQ.empty()) - { - inventory_data& data = mInventoryQ.front(); - - LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString()); - LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString()); - - // Handle addition of texture, if any. - if ( data.mResponse.has("new_texture_folder_id") ) - { - const LLUUID& new_folder_id = data.mResponse["new_texture_folder_id"].asUUID(); - - if ( new_folder_id.notNull() ) - { - LLUUID parent_id = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE); - - std::string name; - // Check if the server built a different name for the texture folder - if ( data.mResponse.has("new_texture_folder_name") ) - { - name = data.mResponse["new_texture_folder_name"].asString(); - } - else - { - name = data.mPostData["name"].asString(); - } - - // Add the category to the internal representation - LLPointer<LLViewerInventoryCategory> cat = - new LLViewerInventoryCategory(new_folder_id, parent_id, - LLFolderType::FT_NONE, name, gAgent.getID()); - cat->setVersion(LLViewerInventoryCategory::VERSION_UNKNOWN); - - LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); - gInventory.accountForUpdate(update); - gInventory.updateCategory(cat); - } - } - - on_new_single_inventory_upload_complete( - asset_type, - inventory_type, - data.mPostData["asset_type"].asString(), - data.mPostData["folder_id"].asUUID(), - data.mPostData["name"], - data.mPostData["description"], - data.mResponse, - data.mResponse["upload_price"]); - //} - - mInventoryQ.pop(); - } - } - - //call completed callbacks on finished decompositions - mDecompThread->notifyCompleted(); - - if (mSkinInfoCullTimer.checkExpirationAndReset(10.f)) - { - //// Clean up dead skin info - //U64Bytes skinbytes(0); - for (auto iter = mSkinMap.begin(), ender = mSkinMap.end(); iter != ender;) - { - auto copy_iter = iter++; - - //skinbytes += U64Bytes(sizeof(LLMeshSkinInfo)); - //skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(std::string)); - //skinbytes += U64Bytes(copy_iter->second->mJointNums.size() * sizeof(S32)); - //skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(LLMatrix4a)); - //skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(LLMatrix4)); - - if (copy_iter->second->getNumRefs() == 1) - { - mSkinMap.erase(copy_iter); - } - } - //LL_INFOS() << "Skin info cache elements:" << mSkinMap.size() << " Memory: " << U64Kilobytes(skinbytes) << LL_ENDL; - } - - // For major operations, attempt to get the required locks - // without blocking and punt if they're not available. The - // longest run of holdoffs is kept in sMaxLockHoldoffs just - // to collect the data. In testing, I've never seen a value - // greater than 2 (written to log on exit). - { - LLMutexTrylock lock1(mMeshMutex); - LLMutexTrylock lock2(mThread->mMutex); - - static U32 hold_offs(0); - if (! lock1.isLocked() || ! lock2.isLocked()) - { - // If we can't get the locks, skip and pick this up later. - ++hold_offs; - sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs); - return; - } - hold_offs = 0; - - if (gAgent.getRegion()) - { - // Update capability urls - static std::string region_name("never name a region this"); - - if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) - { - region_name = gAgent.getRegion()->getName(); - const std::string mesh_cap(gAgent.getRegion()->getViewerAssetUrl()); - mThread->setGetMeshCap(mesh_cap); - LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name - << "', ViewerAsset cap: " << mesh_cap - << LL_ENDL; - } - } - - //popup queued error messages from background threads - while (!mUploadErrorQ.empty()) - { - LLSD substitutions(mUploadErrorQ.front()); - if (substitutions.has("DETAILS")) - { - LLNotificationsUtil::add("MeshUploadErrorDetails", substitutions); - } - else - { - LLNotificationsUtil::add("MeshUploadError", substitutions); - } - mUploadErrorQ.pop(); - } - - S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests; - if (active_count < LLMeshRepoThread::sRequestLowWater) - { - S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count; - - if (mPendingRequests.size() > push_count) - { - // 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) - { - 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()]; - } - - //sort by "score" - std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, - mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); - } - - while (!mPendingRequests.empty() && push_count > 0) - { - LLMeshRepoThread::LODRequest& request = mPendingRequests.front(); - mThread->loadMeshLOD(request.mMeshParams, request.mLOD); - 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()) - { - mThread->loadMeshDecomposition(mPendingDecompositionRequests.front()); - mPendingDecompositionRequests.pop(); - } - - //send physics shapes decomposition requests - while (!mPendingPhysicsShapeRequests.empty()) - { - mThread->loadMeshPhysicsShape(mPendingPhysicsShapeRequests.front()); - mPendingPhysicsShapeRequests.pop(); - } - - mThread->notifyLoadedMeshes(); - } - - mThread->mSignal->signal(); + + //clean up completed upload threads + for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); ) + { + LLMeshUploadThread* thread = *iter; + + if (thread->isStopped() && thread->finished()) + { + iter = mUploads.erase(iter); + delete thread; + } + else + { + ++iter; + } + } + + //update inventory + if (!mInventoryQ.empty()) + { + LLMutexLock lock(mMeshMutex); + while (!mInventoryQ.empty()) + { + inventory_data& data = mInventoryQ.front(); + + LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString()); + LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString()); + + // Handle addition of texture, if any. + if ( data.mResponse.has("new_texture_folder_id") ) + { + const LLUUID& new_folder_id = data.mResponse["new_texture_folder_id"].asUUID(); + + if ( new_folder_id.notNull() ) + { + LLUUID parent_id = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE); + + std::string name; + // Check if the server built a different name for the texture folder + if ( data.mResponse.has("new_texture_folder_name") ) + { + name = data.mResponse["new_texture_folder_name"].asString(); + } + else + { + name = data.mPostData["name"].asString(); + } + + // Add the category to the internal representation + LLPointer<LLViewerInventoryCategory> cat = + new LLViewerInventoryCategory(new_folder_id, parent_id, + LLFolderType::FT_NONE, name, gAgent.getID()); + cat->setVersion(LLViewerInventoryCategory::VERSION_UNKNOWN); + + LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); + gInventory.accountForUpdate(update); + gInventory.updateCategory(cat); + } + } + + on_new_single_inventory_upload_complete( + asset_type, + inventory_type, + data.mPostData["asset_type"].asString(), + data.mPostData["folder_id"].asUUID(), + data.mPostData["name"], + data.mPostData["description"], + data.mResponse, + data.mResponse["upload_price"]); + //} + + mInventoryQ.pop(); + } + } + + //call completed callbacks on finished decompositions + mDecompThread->notifyCompleted(); + + if (mSkinInfoCullTimer.checkExpirationAndReset(10.f)) + { + //// Clean up dead skin info + //U64Bytes skinbytes(0); + for (auto iter = mSkinMap.begin(), ender = mSkinMap.end(); iter != ender;) + { + auto copy_iter = iter++; + + //skinbytes += U64Bytes(sizeof(LLMeshSkinInfo)); + //skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(std::string)); + //skinbytes += U64Bytes(copy_iter->second->mJointNums.size() * sizeof(S32)); + //skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(LLMatrix4a)); + //skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(LLMatrix4)); + + if (copy_iter->second->getNumRefs() == 1) + { + mSkinMap.erase(copy_iter); + } + } + //LL_INFOS() << "Skin info cache elements:" << mSkinMap.size() << " Memory: " << U64Kilobytes(skinbytes) << LL_ENDL; + } + + // For major operations, attempt to get the required locks + // without blocking and punt if they're not available. The + // longest run of holdoffs is kept in sMaxLockHoldoffs just + // to collect the data. In testing, I've never seen a value + // greater than 2 (written to log on exit). + { + LLMutexTrylock lock1(mMeshMutex); + LLMutexTrylock lock2(mThread->mMutex); + + static U32 hold_offs(0); + if (! lock1.isLocked() || ! lock2.isLocked()) + { + // If we can't get the locks, skip and pick this up later. + ++hold_offs; + sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs); + return; + } + hold_offs = 0; + + if (gAgent.getRegion()) + { + // Update capability urls + static std::string region_name("never name a region this"); + + if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) + { + region_name = gAgent.getRegion()->getName(); + const std::string mesh_cap(gAgent.getRegion()->getViewerAssetUrl()); + mThread->setGetMeshCap(mesh_cap); + LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name + << "', ViewerAsset cap: " << mesh_cap + << LL_ENDL; + } + } + + //popup queued error messages from background threads + while (!mUploadErrorQ.empty()) + { + LLSD substitutions(mUploadErrorQ.front()); + if (substitutions.has("DETAILS")) + { + LLNotificationsUtil::add("MeshUploadErrorDetails", substitutions); + } + else + { + LLNotificationsUtil::add("MeshUploadError", substitutions); + } + mUploadErrorQ.pop(); + } + + S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests; + if (active_count < LLMeshRepoThread::sRequestLowWater) + { + S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count; + + if (mPendingRequests.size() > push_count) + { + // 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) + { + 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()]; + } + + //sort by "score" + std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, + mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); + } + + while (!mPendingRequests.empty() && push_count > 0) + { + LLMeshRepoThread::LODRequest& request = mPendingRequests.front(); + mThread->loadMeshLOD(request.mMeshParams, request.mLOD); + 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()) + { + mThread->loadMeshDecomposition(mPendingDecompositionRequests.front()); + mPendingDecompositionRequests.pop(); + } + + //send physics shapes decomposition requests + while (!mPendingPhysicsShapeRequests.empty()) + { + mThread->loadMeshPhysicsShape(mPendingPhysicsShapeRequests.front()); + mPendingPhysicsShapeRequests.pop(); + } + + mThread->notifyLoadedMeshes(); + } + + mThread->mSignal->signal(); } void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo* info) { - mSkinMap[info->mMeshID] = info; // Cache into LLPointer + mSkinMap[info->mMeshID] = info; // Cache into LLPointer // Alternative: We can get skin size from header sCacheBytesSkins += info->sizeBytes(); - skin_load_map::iterator iter = mLoadingSkins.find(info->mMeshID); - if (iter != mLoadingSkins.end()) - { - for (LLVOVolume* vobj : iter->second) - { - if (vobj) - { - vobj->notifySkinInfoLoaded(info); - } - } - mLoadingSkins.erase(iter); - } + skin_load_map::iterator iter = mLoadingSkins.find(info->mMeshID); + if (iter != mLoadingSkins.end()) + { + for (LLVOVolume* vobj : iter->second) + { + if (vobj) + { + vobj->notifySkinInfoLoaded(info); + } + } + mLoadingSkins.erase(iter); + } } 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) - { - if (vobj) - { - vobj->notifySkinInfoUnavailable(); - } - } - mLoadingSkins.erase(iter); - } + skin_load_map::iterator iter = mLoadingSkins.find(mesh_id); + if (iter != mLoadingSkins.end()) + { + for (LLVOVolume* vobj : iter->second) + { + if (vobj) + { + vobj->notifySkinInfoUnavailable(); + } + } + mLoadingSkins.erase(iter); + } } void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp) { - decomposition_map::iterator iter = mDecompositionMap.find(decomp->mMeshID); - if (iter == mDecompositionMap.end()) - { //just insert decomp into map - mDecompositionMap[decomp->mMeshID] = decomp; - mLoadingDecompositions.erase(decomp->mMeshID); + decomposition_map::iterator iter = mDecompositionMap.find(decomp->mMeshID); + if (iter == mDecompositionMap.end()) + { //just insert decomp into map + mDecompositionMap[decomp->mMeshID] = decomp; + mLoadingDecompositions.erase(decomp->mMeshID); sCacheBytesDecomps += decomp->sizeBytes(); - } - else - { //merge decomp with existing entry + } + else + { //merge decomp with existing entry sCacheBytesDecomps -= iter->second->sizeBytes(); - iter->second->merge(decomp); + iter->second->merge(decomp); sCacheBytesDecomps += iter->second->sizeBytes(); - mLoadingDecompositions.erase(decomp->mMeshID); - delete decomp; - } + mLoadingDecompositions.erase(decomp->mMeshID); + delete decomp; + } } void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) { //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); - - if (volume && obj_iter != mLoadingMeshes[detail].end()) - { - //make sure target volume is still valid - if (volume->getNumVolumeFaces() <= 0) - { - LL_WARNS(LOG_MESH) << "Mesh loading returned empty volume. ID: " << mesh_id - << LL_ENDL; - } - - { //update system volume - LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail); - if (sys_volume) - { - sys_volume->copyVolumeFaces(volume); - sys_volume->setMeshAssetLoaded(true); - LLPrimitive::getVolumeManager()->unrefVolume(sys_volume); - } - else - { - LL_WARNS(LOG_MESH) << "Couldn't find system volume for mesh " << mesh_id - << LL_ENDL; - } - } - - //notify waiting LLVOVolume instances that their requested mesh is available - for (LLVOVolume* vobj : obj_iter->second) - { - if (vobj) - { - vobj->notifyMeshLoaded(); - } - } - - mLoadingMeshes[detail].erase(obj_iter); - - LLViewerStatsRecorder::instance().meshLoaded(); - } + 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); + + if (volume && obj_iter != mLoadingMeshes[detail].end()) + { + //make sure target volume is still valid + if (volume->getNumVolumeFaces() <= 0) + { + LL_WARNS(LOG_MESH) << "Mesh loading returned empty volume. ID: " << mesh_id + << LL_ENDL; + } + + { //update system volume + LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail); + if (sys_volume) + { + sys_volume->copyVolumeFaces(volume); + sys_volume->setMeshAssetLoaded(true); + LLPrimitive::getVolumeManager()->unrefVolume(sys_volume); + } + else + { + LL_WARNS(LOG_MESH) << "Couldn't find system volume for mesh " << mesh_id + << LL_ENDL; + } + } + + //notify waiting LLVOVolume instances that their requested mesh is available + for (LLVOVolume* vobj : obj_iter->second) + { + if (vobj) + { + vobj->notifyMeshLoaded(); + } + } + + mLoadingMeshes[detail].erase(obj_iter); + + LLViewerStatsRecorder::instance().meshLoaded(); + } } void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 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()) - { - F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); + //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()) + { + F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, lod); if (sys_volume) @@ -4109,28 +4109,28 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, LLPrimitive::getVolumeManager()->unrefVolume(sys_volume); } - for (LLVOVolume* vobj : obj_iter->second) - { - if (vobj) - { - LLVolume* obj_volume = vobj->getVolume(); + for (LLVOVolume* vobj : obj_iter->second) + { + if (vobj) + { + LLVolume* obj_volume = vobj->getVolume(); + + if (obj_volume && + obj_volume->getDetail() == detail && + obj_volume->getParams() == mesh_params) + { //should force volume to find most appropriate LOD + vobj->setVolume(obj_volume->getParams(), lod); + } + } + } - if (obj_volume && - obj_volume->getDetail() == detail && - obj_volume->getParams() == mesh_params) - { //should force volume to find most appropriate LOD - vobj->setVolume(obj_volume->getParams(), lod); - } - } - } - - mLoadingMeshes[lod].erase(obj_iter); - } + mLoadingMeshes[lod].erase(obj_iter); + } } S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) -{ - return mThread->getActualMeshLOD(mesh_params, lod); +{ + return mThread->getActualMeshLOD(mesh_params, lod); } const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj) @@ -4150,98 +4150,98 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV LLMutexLock lock(mMeshMutex); //add volume to list of loading meshes 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); - } - } - else - { - //first request for this mesh - mLoadingSkins[mesh_id].push_back(requesting_obj); + 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); + } + } + else + { + //first request for this mesh + mLoadingSkins[mesh_id].push_back(requesting_obj); mPendingSkinRequests.push(mesh_id); } } } - return nullptr; + return nullptr; } void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id) { LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH); - if (mesh_id.notNull()) - { - LLModel::Decomposition* decomp = NULL; - decomposition_map::iterator iter = mDecompositionMap.find(mesh_id); - if (iter != mDecompositionMap.end()) - { - decomp = iter->second; - } - - //decomposition block hasn't been fetched yet - if (!decomp || decomp->mPhysicsShapeMesh.empty()) - { - LLMutexLock lock(mMeshMutex); - //add volume to list of loading meshes - std::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); - } - } - } + if (mesh_id.notNull()) + { + LLModel::Decomposition* decomp = NULL; + decomposition_map::iterator iter = mDecompositionMap.find(mesh_id); + if (iter != mDecompositionMap.end()) + { + decomp = iter->second; + } + + //decomposition block hasn't been fetched yet + if (!decomp || decomp->mPhysicsShapeMesh.empty()) + { + LLMutexLock lock(mMeshMutex); + //add volume to list of loading meshes + std::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); + } + } + } } LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id) { LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH); - LLModel::Decomposition* ret = NULL; - - if (mesh_id.notNull()) - { - decomposition_map::iterator iter = mDecompositionMap.find(mesh_id); - if (iter != mDecompositionMap.end()) - { - ret = iter->second; - } - - //decomposition block hasn't been fetched yet - if (!ret || ret->mBaseHullMesh.empty()) - { - LLMutexLock lock(mMeshMutex); - //add volume to list of loading meshes - std::set<LLUUID>::iterator iter = mLoadingDecompositions.find(mesh_id); - if (iter == mLoadingDecompositions.end()) - { //no request pending for this skin info - mLoadingDecompositions.insert(mesh_id); - mPendingDecompositionRequests.push(mesh_id); - } - } - } - - return ret; + LLModel::Decomposition* ret = NULL; + + if (mesh_id.notNull()) + { + decomposition_map::iterator iter = mDecompositionMap.find(mesh_id); + if (iter != mDecompositionMap.end()) + { + ret = iter->second; + } + + //decomposition block hasn't been fetched yet + if (!ret || ret->mBaseHullMesh.empty()) + { + LLMutexLock lock(mMeshMutex); + //add volume to list of loading meshes + std::set<LLUUID>::iterator iter = mLoadingDecompositions.find(mesh_id); + if (iter == mLoadingDecompositions.end()) + { //no request pending for this skin info + mLoadingDecompositions.insert(mesh_id); + mPendingDecompositionRequests.push(mesh_id); + } + } + } + + return ret; } void LLMeshRepository::buildHull(const LLVolumeParams& params, S32 detail) { - LLVolume* volume = LLPrimitive::sVolumeManager->refVolume(params, detail); + LLVolume* volume = LLPrimitive::sVolumeManager->refVolume(params, detail); - if (!volume->mHullPoints) - { - //all default params - //execute first stage - //set simplify mode to retain - //set retain percentage to zero - //run second stage - } + if (!volume->mHullPoints) + { + //all default params + //execute first stage + //set simplify mode to retain + //set retain percentage to zero + //run second stage + } - LLPrimitive::sVolumeManager->unrefVolume(volume); + LLPrimitive::sVolumeManager->unrefVolume(volume); } bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id) @@ -4337,93 +4337,93 @@ bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) } void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures, - bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, + bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, std::string upload_url, bool do_upload, - LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer) + LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer) { - LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, - upload_skin, upload_joints, lock_scale_if_joint_position, + LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, + upload_skin, upload_joints, lock_scale_if_joint_position, upload_url, do_upload, fee_observer, upload_observer); - mUploadWaitList.push_back(thread); + mUploadWaitList.push_back(thread); } S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) { 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) - { - const LLMeshHeader& header = iter->second.second; - - if (header.m404) - { - return -1; - } + 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) + { + const LLMeshHeader& header = iter->second.second; + + if (header.m404) + { + return -1; + } S32 size = header.mLodSize[lod]; - return size; - } + return size; + } - } + } - return -1; + return -1; } void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation, - LLVector3& result_pos, - LLQuaternion& result_rot, - LLVector3& result_scale) + LLVector3& result_pos, + LLQuaternion& result_rot, + LLVector3& result_scale) { - // check for reflection - BOOL reflected = (transformation.determinant() < 0); + // check for reflection + BOOL reflected = (transformation.determinant() < 0); - // compute position - LLVector3 position = LLVector3(0, 0, 0) * transformation; + // compute position + LLVector3 position = LLVector3(0, 0, 0) * transformation; - // compute scale - LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); - LLVector3 scale = LLVector3(x_length, y_length, z_length); + // compute scale + LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + LLVector3 scale = LLVector3(x_length, y_length, z_length); // adjust for "reflected" geometry - LLVector3 x_transformed_reflected = x_transformed; - if (reflected) - { - x_transformed_reflected *= -1.0; - } - - // compute rotation - LLMatrix3 rotation_matrix; - rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed); - LLQuaternion quat_rotation = rotation_matrix.quaternion(); - quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here. - LLVector3 euler_rotation; - quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]); - - result_pos = position + mOrigin; - result_scale = scale; - result_rot = quat_rotation; + LLVector3 x_transformed_reflected = x_transformed; + if (reflected) + { + x_transformed_reflected *= -1.0; + } + + // compute rotation + LLMatrix3 rotation_matrix; + rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed); + LLQuaternion quat_rotation = rotation_matrix.quaternion(); + quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here. + LLVector3 euler_rotation; + quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]); + + result_pos = position + mOrigin; + result_scale = scale; + result_rot = quat_rotation; } void LLMeshRepository::updateInventory(inventory_data data) { - LLMutexLock lock(mMeshMutex); - dump_llsd_to_file(data.mPostData,make_dump_name("update_inventory_post_data_",dump_num)); - dump_llsd_to_file(data.mResponse,make_dump_name("update_inventory_response_",dump_num)); - mInventoryQ.push(data); + LLMutexLock lock(mMeshMutex); + dump_llsd_to_file(data.mPostData,make_dump_name("update_inventory_post_data_",dump_num)); + dump_llsd_to_file(data.mResponse,make_dump_name("update_inventory_response_",dump_num)); + mInventoryQ.push(data); } void LLMeshRepository::uploadError(LLSD& args) { - LLMutexLock lock(mMeshMutex); - mUploadErrorQ.push(args); + LLMutexLock lock(mMeshMutex); + mUploadErrorQ.push(args); } F32 LLMeshRepository::getEstTrianglesMax(LLUUID mesh_id) @@ -4455,7 +4455,7 @@ F32 LLMeshRepository::getEstTrianglesStreamingCost(LLUUID mesh_id) // FIXME replace with calc based on LLMeshCostData F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value) { - F32 result = 0.f; + F32 result = 0.f; if (mThread && mesh_id.notNull()) { LLMutexLock lock(mThread->mHeaderMutex); @@ -4501,109 +4501,109 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* by //static F32 LLMeshRepository::getStreamingCostLegacy(LLMeshHeader& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value) { - if (header.m404 - || header.mLodSize[0] <= 0 - || (header.mVersion > MAX_MESH_VERSION)) - { - return 0.f; - } - - F32 max_distance = 512.f; - - F32 dlowest = llmin(radius/0.03f, max_distance); - F32 dlow = llmin(radius/0.06f, max_distance); - F32 dmid = llmin(radius/0.24f, max_distance); - - static LLCachedControl<U32> metadata_discount_ch(gSavedSettings, "MeshMetaDataDiscount", 384); //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead - static LLCachedControl<U32> minimum_size_ch(gSavedSettings, "MeshMinimumByteSize", 16); //make sure nothing is "free" - static LLCachedControl<U32> bytes_per_triangle_ch(gSavedSettings, "MeshBytesPerTriangle", 16); - - F32 metadata_discount = (F32)metadata_discount_ch; - F32 minimum_size = (F32)minimum_size_ch; - F32 bytes_per_triangle = (F32)bytes_per_triangle_ch; - - S32 bytes_lowest = header.mLodSize[0]; - S32 bytes_low = header.mLodSize[1]; - S32 bytes_mid = header.mLodSize[2]; - S32 bytes_high = header.mLodSize[3]; - - if (bytes_high == 0) - { - return 0.f; - } - - if (bytes_mid == 0) - { - bytes_mid = bytes_high; - } - - if (bytes_low == 0) - { - bytes_low = bytes_mid; - } - - if (bytes_lowest == 0) - { - bytes_lowest = bytes_low; - } - - F32 triangles_lowest = llmax((F32) bytes_lowest-metadata_discount, minimum_size)/bytes_per_triangle; - F32 triangles_low = llmax((F32) bytes_low-metadata_discount, minimum_size)/bytes_per_triangle; - F32 triangles_mid = llmax((F32) bytes_mid-metadata_discount, minimum_size)/bytes_per_triangle; - F32 triangles_high = llmax((F32) bytes_high-metadata_discount, minimum_size)/bytes_per_triangle; - - if (bytes) - { - *bytes = 0; - *bytes += header.mLodSize[0]; - *bytes += header.mLodSize[1]; - *bytes += header.mLodSize[2]; - *bytes += header.mLodSize[3]; - } - - if (bytes_visible) - { - lod = LLMeshRepository::getActualMeshLOD(header, lod); - if (lod >= 0 && lod <= 3) - { - *bytes_visible = header.mLodSize[lod]; - } - } - - F32 max_area = 102944.f; //area of circle that encompasses region (see MAINT-6559) - F32 min_area = 1.f; - - F32 high_area = llmin(F_PI*dmid*dmid, max_area); - F32 mid_area = llmin(F_PI*dlow*dlow, max_area); - F32 low_area = llmin(F_PI*dlowest*dlowest, max_area); - F32 lowest_area = max_area; - - lowest_area -= low_area; - low_area -= mid_area; - mid_area -= high_area; - - high_area = llclamp(high_area, min_area, max_area); - mid_area = llclamp(mid_area, min_area, max_area); - low_area = llclamp(low_area, min_area, max_area); - lowest_area = llclamp(lowest_area, min_area, max_area); - - F32 total_area = high_area + mid_area + low_area + lowest_area; - high_area /= total_area; - mid_area /= total_area; - low_area /= total_area; - lowest_area /= total_area; - - F32 weighted_avg = triangles_high*high_area + - triangles_mid*mid_area + - triangles_low*low_area + - triangles_lowest*lowest_area; - - if (unscaled_value) - { - *unscaled_value = weighted_avg; - } - - return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f; + if (header.m404 + || header.mLodSize[0] <= 0 + || (header.mVersion > MAX_MESH_VERSION)) + { + return 0.f; + } + + F32 max_distance = 512.f; + + F32 dlowest = llmin(radius/0.03f, max_distance); + F32 dlow = llmin(radius/0.06f, max_distance); + F32 dmid = llmin(radius/0.24f, max_distance); + + static LLCachedControl<U32> metadata_discount_ch(gSavedSettings, "MeshMetaDataDiscount", 384); //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead + static LLCachedControl<U32> minimum_size_ch(gSavedSettings, "MeshMinimumByteSize", 16); //make sure nothing is "free" + static LLCachedControl<U32> bytes_per_triangle_ch(gSavedSettings, "MeshBytesPerTriangle", 16); + + F32 metadata_discount = (F32)metadata_discount_ch; + F32 minimum_size = (F32)minimum_size_ch; + F32 bytes_per_triangle = (F32)bytes_per_triangle_ch; + + S32 bytes_lowest = header.mLodSize[0]; + S32 bytes_low = header.mLodSize[1]; + S32 bytes_mid = header.mLodSize[2]; + S32 bytes_high = header.mLodSize[3]; + + if (bytes_high == 0) + { + return 0.f; + } + + if (bytes_mid == 0) + { + bytes_mid = bytes_high; + } + + if (bytes_low == 0) + { + bytes_low = bytes_mid; + } + + if (bytes_lowest == 0) + { + bytes_lowest = bytes_low; + } + + F32 triangles_lowest = llmax((F32) bytes_lowest-metadata_discount, minimum_size)/bytes_per_triangle; + F32 triangles_low = llmax((F32) bytes_low-metadata_discount, minimum_size)/bytes_per_triangle; + F32 triangles_mid = llmax((F32) bytes_mid-metadata_discount, minimum_size)/bytes_per_triangle; + F32 triangles_high = llmax((F32) bytes_high-metadata_discount, minimum_size)/bytes_per_triangle; + + if (bytes) + { + *bytes = 0; + *bytes += header.mLodSize[0]; + *bytes += header.mLodSize[1]; + *bytes += header.mLodSize[2]; + *bytes += header.mLodSize[3]; + } + + if (bytes_visible) + { + lod = LLMeshRepository::getActualMeshLOD(header, lod); + if (lod >= 0 && lod <= 3) + { + *bytes_visible = header.mLodSize[lod]; + } + } + + F32 max_area = 102944.f; //area of circle that encompasses region (see MAINT-6559) + F32 min_area = 1.f; + + F32 high_area = llmin(F_PI*dmid*dmid, max_area); + F32 mid_area = llmin(F_PI*dlow*dlow, max_area); + F32 low_area = llmin(F_PI*dlowest*dlowest, max_area); + F32 lowest_area = max_area; + + lowest_area -= low_area; + low_area -= mid_area; + mid_area -= high_area; + + high_area = llclamp(high_area, min_area, max_area); + mid_area = llclamp(mid_area, min_area, max_area); + low_area = llclamp(low_area, min_area, max_area); + lowest_area = llclamp(lowest_area, min_area, max_area); + + F32 total_area = high_area + mid_area + low_area + lowest_area; + high_area /= total_area; + mid_area /= total_area; + low_area /= total_area; + lowest_area /= total_area; + + F32 weighted_avg = triangles_high*high_area + + triangles_mid*mid_area + + triangles_low*low_area + + triangles_lowest*lowest_area; + + if (unscaled_value) + { + *unscaled_value = weighted_avg; + } + + return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f; } LLMeshCostData::LLMeshCostData() @@ -4615,10 +4615,10 @@ LLMeshCostData::LLMeshCostData() bool LLMeshCostData::init(const LLMeshHeader& header) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; - + std::fill(mSizeByLOD.begin(), mSizeByLOD.end(), 0); std::fill(mEstTrisByLOD.begin(), mEstTrisByLOD.end(), 0.f); - + S32 bytes_high = header.mLodSize[3]; S32 bytes_med = header.mLodSize[2]; if (bytes_med == 0) @@ -4683,44 +4683,44 @@ F32 LLMeshCostData::getEstTrisMax() F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) { - F32 max_distance = 512.f; - - F32 dlowest = llmin(radius/0.03f, max_distance); - F32 dlow = llmin(radius/0.06f, max_distance); - F32 dmid = llmin(radius/0.24f, max_distance); - - F32 triangles_lowest = mEstTrisByLOD[0]; - F32 triangles_low = mEstTrisByLOD[1]; - F32 triangles_mid = mEstTrisByLOD[2]; - F32 triangles_high = mEstTrisByLOD[3]; - - F32 max_area = 102944.f; //area of circle that encompasses region (see MAINT-6559) - F32 min_area = 1.f; - - F32 high_area = llmin(F_PI*dmid*dmid, max_area); - F32 mid_area = llmin(F_PI*dlow*dlow, max_area); - F32 low_area = llmin(F_PI*dlowest*dlowest, max_area); - F32 lowest_area = max_area; - - lowest_area -= low_area; - low_area -= mid_area; - mid_area -= high_area; - - high_area = llclamp(high_area, min_area, max_area); - mid_area = llclamp(mid_area, min_area, max_area); - low_area = llclamp(low_area, min_area, max_area); - lowest_area = llclamp(lowest_area, min_area, max_area); - - F32 total_area = high_area + mid_area + low_area + lowest_area; - high_area /= total_area; - mid_area /= total_area; - low_area /= total_area; - lowest_area /= total_area; - - F32 weighted_avg = triangles_high*high_area + - triangles_mid*mid_area + - triangles_low*low_area + - triangles_lowest*lowest_area; + F32 max_distance = 512.f; + + F32 dlowest = llmin(radius/0.03f, max_distance); + F32 dlow = llmin(radius/0.06f, max_distance); + F32 dmid = llmin(radius/0.24f, max_distance); + + F32 triangles_lowest = mEstTrisByLOD[0]; + F32 triangles_low = mEstTrisByLOD[1]; + F32 triangles_mid = mEstTrisByLOD[2]; + F32 triangles_high = mEstTrisByLOD[3]; + + F32 max_area = 102944.f; //area of circle that encompasses region (see MAINT-6559) + F32 min_area = 1.f; + + F32 high_area = llmin(F_PI*dmid*dmid, max_area); + F32 mid_area = llmin(F_PI*dlow*dlow, max_area); + F32 low_area = llmin(F_PI*dlowest*dlowest, max_area); + F32 lowest_area = max_area; + + lowest_area -= low_area; + low_area -= mid_area; + mid_area -= high_area; + + high_area = llclamp(high_area, min_area, max_area); + mid_area = llclamp(mid_area, min_area, max_area); + low_area = llclamp(low_area, min_area, max_area); + lowest_area = llclamp(lowest_area, min_area, max_area); + + F32 total_area = high_area + mid_area + low_area + lowest_area; + high_area /= total_area; + mid_area /= total_area; + low_area /= total_area; + lowest_area /= total_area; + + F32 weighted_avg = triangles_high*high_area + + triangles_mid*mid_area + + triangles_low*low_area + + triangles_lowest*lowest_area; return weighted_avg; } @@ -4754,7 +4754,7 @@ F32 LLMeshCostData::getEstTrisForStreamingCost() F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) { - return getRadiusWeightedTris(radius)/gSavedSettings.getU32("MeshTriangleBudget")*15000.f; + return getRadiusWeightedTris(radius)/gSavedSettings.getU32("MeshTriangleBudget")*15000.f; } F32 LLMeshCostData::getTriangleBasedStreamingCost() @@ -4767,7 +4767,7 @@ bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; data = LLMeshCostData(); - + if (mThread && mesh_id.notNull()) { LLMutexLock lock(mThread->mHeaderMutex); @@ -4798,606 +4798,606 @@ bool LLMeshRepository::getCostData(LLMeshHeader& header, LLMeshCostData& data) { return false; } - + return true; } LLPhysicsDecomp::LLPhysicsDecomp() : LLThread("Physics Decomp") { - mInited = false; - mQuitting = false; - mDone = false; + mInited = false; + mQuitting = false; + mDone = false; - mSignal = new LLCondition(); - mMutex = new LLMutex(); + mSignal = new LLCondition(); + mMutex = new LLMutex(); } LLPhysicsDecomp::~LLPhysicsDecomp() { - shutdown(); + shutdown(); - delete mSignal; - mSignal = NULL; - delete mMutex; - mMutex = NULL; + delete mSignal; + mSignal = NULL; + delete mMutex; + mMutex = NULL; } void LLPhysicsDecomp::shutdown() { - if (mSignal) - { - mQuitting = true; - // There is only one wait(), but just in case 'broadcast' - mSignal->broadcast(); + if (mSignal) + { + mQuitting = true; + // There is only one wait(), but just in case 'broadcast' + mSignal->broadcast(); - while (!isStopped()) - { - apr_sleep(10); - } - } + while (!isStopped()) + { + apr_sleep(10); + } + } } void LLPhysicsDecomp::submitRequest(LLPhysicsDecomp::Request* request) { - LLMutexLock lock(mMutex); - mRequestQ.push(request); - mSignal->signal(); + LLMutexLock lock(mMutex); + mRequestQ.push(request); + mSignal->signal(); } //static S32 LLPhysicsDecomp::llcdCallback(const char* status, S32 p1, S32 p2) -{ - if (gMeshRepo.mDecompThread && gMeshRepo.mDecompThread->mCurRequest.notNull()) - { - return gMeshRepo.mDecompThread->mCurRequest->statusCallback(status, p1, p2); - } +{ + if (gMeshRepo.mDecompThread && gMeshRepo.mDecompThread->mCurRequest.notNull()) + { + return gMeshRepo.mDecompThread->mCurRequest->statusCallback(status, p1, p2); + } - return 1; + return 1; } void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh, bool vertex_based) { - mesh.mVertexBase = mCurRequest->mPositions[0].mV; - mesh.mVertexStrideBytes = 12; - mesh.mNumVertices = mCurRequest->mPositions.size(); - - if(!vertex_based) - { - mesh.mIndexType = LLCDMeshData::INT_16; - mesh.mIndexBase = &(mCurRequest->mIndices[0]); - mesh.mIndexStrideBytes = 6; - - mesh.mNumTriangles = mCurRequest->mIndices.size()/3; - } - - if ((vertex_based || mesh.mNumTriangles > 0) && mesh.mNumVertices > 2) - { - LLCDResult ret = LLCD_OK; - if (LLConvexDecomposition::getInstance() != NULL) - { - ret = LLConvexDecomposition::getInstance()->setMeshData(&mesh, vertex_based); - } - - if (ret) - { - LL_ERRS(LOG_MESH) << "Convex Decomposition thread valid but could not set mesh data." << LL_ENDL; - } - } + mesh.mVertexBase = mCurRequest->mPositions[0].mV; + mesh.mVertexStrideBytes = 12; + mesh.mNumVertices = mCurRequest->mPositions.size(); + + if(!vertex_based) + { + mesh.mIndexType = LLCDMeshData::INT_16; + mesh.mIndexBase = &(mCurRequest->mIndices[0]); + mesh.mIndexStrideBytes = 6; + + mesh.mNumTriangles = mCurRequest->mIndices.size()/3; + } + + if ((vertex_based || mesh.mNumTriangles > 0) && mesh.mNumVertices > 2) + { + LLCDResult ret = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + ret = LLConvexDecomposition::getInstance()->setMeshData(&mesh, vertex_based); + } + + if (ret) + { + LL_ERRS(LOG_MESH) << "Convex Decomposition thread valid but could not set mesh data." << LL_ENDL; + } + } } void LLPhysicsDecomp::doDecomposition() { - LLCDMeshData mesh; - S32 stage = mStageID[mCurRequest->mStage]; - - if (LLConvexDecomposition::getInstance() == NULL) - { - // stub library. do nothing. - return; - } - - //load data intoLLCD - if (stage == 0) - { - setMeshData(mesh, false); - } - - //build parameter map - std::map<std::string, const LLCDParam*> param_map; - - static const LLCDParam* params = NULL; - static S32 param_count = 0; - if (!params) - { - param_count = LLConvexDecomposition::getInstance()->getParameters(¶ms); - } - - for (S32 i = 0; i < param_count; ++i) - { - param_map[params[i].mName] = params+i; - } - - U32 ret = LLCD_OK; - //set parameter values - for (decomp_params::iterator iter = mCurRequest->mParams.begin(); iter != mCurRequest->mParams.end(); ++iter) - { - const std::string& name = iter->first; - const LLSD& value = iter->second; - - const LLCDParam* param = param_map[name]; - - if (param == NULL) - { //couldn't find valid parameter - continue; - } - - - if (param->mType == LLCDParam::LLCD_FLOAT) - { - ret = LLConvexDecomposition::getInstance()->setParam(param->mName, (F32) value.asReal()); - } - else if (param->mType == LLCDParam::LLCD_INTEGER || - param->mType == LLCDParam::LLCD_ENUM) - { - ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asInteger()); - } - else if (param->mType == LLCDParam::LLCD_BOOLEAN) - { - ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asBoolean()); - } - } - - mCurRequest->setStatusMessage("Executing."); - - if (LLConvexDecomposition::getInstance() != NULL) - { - ret = LLConvexDecomposition::getInstance()->executeStage(stage); - } - - if (ret) - { - LL_WARNS(LOG_MESH) << "Convex Decomposition thread valid but could not execute stage " << stage << "." - << LL_ENDL; - LLMutexLock lock(mMutex); - - mCurRequest->mHull.clear(); - mCurRequest->mHullMesh.clear(); - - mCurRequest->setStatusMessage("FAIL"); - - completeCurrent(); - } - else - { - mCurRequest->setStatusMessage("Reading results"); - - S32 num_hulls =0; - if (LLConvexDecomposition::getInstance() != NULL) - { - num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(stage); - } - - { - LLMutexLock lock(mMutex); - mCurRequest->mHull.clear(); - mCurRequest->mHull.resize(num_hulls); - - mCurRequest->mHullMesh.clear(); - mCurRequest->mHullMesh.resize(num_hulls); - } - - for (S32 i = 0; i < num_hulls; ++i) - { - std::vector<LLVector3> p; - LLCDHull hull; - // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code - LLConvexDecomposition::getInstance()->getHullFromStage(stage, i, &hull); - - const F32* v = hull.mVertexBase; - - for (S32 j = 0; j < hull.mNumVertices; ++j) - { - LLVector3 vert(v[0], v[1], v[2]); - p.push_back(vert); - v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); - } - - LLCDMeshData mesh; - // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code - LLConvexDecomposition::getInstance()->getMeshFromStage(stage, i, &mesh); - - get_vertex_buffer_from_mesh(mesh, mCurRequest->mHullMesh[i]); - - { - LLMutexLock lock(mMutex); - mCurRequest->mHull[i] = p; - } - } - - { - LLMutexLock lock(mMutex); - mCurRequest->setStatusMessage("FAIL"); - completeCurrent(); - } - } + LLCDMeshData mesh; + S32 stage = mStageID[mCurRequest->mStage]; + + if (LLConvexDecomposition::getInstance() == NULL) + { + // stub library. do nothing. + return; + } + + //load data intoLLCD + if (stage == 0) + { + setMeshData(mesh, false); + } + + //build parameter map + std::map<std::string, const LLCDParam*> param_map; + + static const LLCDParam* params = NULL; + static S32 param_count = 0; + if (!params) + { + param_count = LLConvexDecomposition::getInstance()->getParameters(¶ms); + } + + for (S32 i = 0; i < param_count; ++i) + { + param_map[params[i].mName] = params+i; + } + + U32 ret = LLCD_OK; + //set parameter values + for (decomp_params::iterator iter = mCurRequest->mParams.begin(); iter != mCurRequest->mParams.end(); ++iter) + { + const std::string& name = iter->first; + const LLSD& value = iter->second; + + const LLCDParam* param = param_map[name]; + + if (param == NULL) + { //couldn't find valid parameter + continue; + } + + + if (param->mType == LLCDParam::LLCD_FLOAT) + { + ret = LLConvexDecomposition::getInstance()->setParam(param->mName, (F32) value.asReal()); + } + else if (param->mType == LLCDParam::LLCD_INTEGER || + param->mType == LLCDParam::LLCD_ENUM) + { + ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asInteger()); + } + else if (param->mType == LLCDParam::LLCD_BOOLEAN) + { + ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asBoolean()); + } + } + + mCurRequest->setStatusMessage("Executing."); + + if (LLConvexDecomposition::getInstance() != NULL) + { + ret = LLConvexDecomposition::getInstance()->executeStage(stage); + } + + if (ret) + { + LL_WARNS(LOG_MESH) << "Convex Decomposition thread valid but could not execute stage " << stage << "." + << LL_ENDL; + LLMutexLock lock(mMutex); + + mCurRequest->mHull.clear(); + mCurRequest->mHullMesh.clear(); + + mCurRequest->setStatusMessage("FAIL"); + + completeCurrent(); + } + else + { + mCurRequest->setStatusMessage("Reading results"); + + S32 num_hulls =0; + if (LLConvexDecomposition::getInstance() != NULL) + { + num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(stage); + } + + { + LLMutexLock lock(mMutex); + mCurRequest->mHull.clear(); + mCurRequest->mHull.resize(num_hulls); + + mCurRequest->mHullMesh.clear(); + mCurRequest->mHullMesh.resize(num_hulls); + } + + for (S32 i = 0; i < num_hulls; ++i) + { + std::vector<LLVector3> p; + LLCDHull hull; + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + LLConvexDecomposition::getInstance()->getHullFromStage(stage, i, &hull); + + const F32* v = hull.mVertexBase; + + for (S32 j = 0; j < hull.mNumVertices; ++j) + { + LLVector3 vert(v[0], v[1], v[2]); + p.push_back(vert); + v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); + } + + LLCDMeshData mesh; + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + LLConvexDecomposition::getInstance()->getMeshFromStage(stage, i, &mesh); + + get_vertex_buffer_from_mesh(mesh, mCurRequest->mHullMesh[i]); + + { + LLMutexLock lock(mMutex); + mCurRequest->mHull[i] = p; + } + } + + { + LLMutexLock lock(mMutex); + mCurRequest->setStatusMessage("FAIL"); + completeCurrent(); + } + } } void LLPhysicsDecomp::completeCurrent() { - LLMutexLock lock(mMutex); - mCompletedQ.push(mCurRequest); - mCurRequest = NULL; + LLMutexLock lock(mMutex); + mCompletedQ.push(mCurRequest); + mCurRequest = NULL; } void LLPhysicsDecomp::notifyCompleted() { - if (!mCompletedQ.empty()) - { - LLMutexLock lock(mMutex); - while (!mCompletedQ.empty()) - { - Request* req = mCompletedQ.front(); - req->completed(); - mCompletedQ.pop(); - } - } + if (!mCompletedQ.empty()) + { + LLMutexLock lock(mMutex); + while (!mCompletedQ.empty()) + { + Request* req = mCompletedQ.front(); + req->completed(); + mCompletedQ.pop(); + } + } } void make_box(LLPhysicsDecomp::Request * request) { - LLVector3 min,max; - min = request->mPositions[0]; - max = min; + LLVector3 min,max; + min = request->mPositions[0]; + max = min; + + for (U32 i = 0; i < request->mPositions.size(); ++i) + { + update_min_max(min, max, request->mPositions[i]); + } - for (U32 i = 0; i < request->mPositions.size(); ++i) - { - update_min_max(min, max, request->mPositions[i]); - } + request->mHull.clear(); - request->mHull.clear(); - - LLModel::hull box; - box.push_back(LLVector3(min[0],min[1],min[2])); - box.push_back(LLVector3(max[0],min[1],min[2])); - box.push_back(LLVector3(min[0],max[1],min[2])); - box.push_back(LLVector3(max[0],max[1],min[2])); - box.push_back(LLVector3(min[0],min[1],max[2])); - box.push_back(LLVector3(max[0],min[1],max[2])); - box.push_back(LLVector3(min[0],max[1],max[2])); - box.push_back(LLVector3(max[0],max[1],max[2])); + LLModel::hull box; + box.push_back(LLVector3(min[0],min[1],min[2])); + box.push_back(LLVector3(max[0],min[1],min[2])); + box.push_back(LLVector3(min[0],max[1],min[2])); + box.push_back(LLVector3(max[0],max[1],min[2])); + box.push_back(LLVector3(min[0],min[1],max[2])); + box.push_back(LLVector3(max[0],min[1],max[2])); + box.push_back(LLVector3(min[0],max[1],max[2])); + box.push_back(LLVector3(max[0],max[1],max[2])); - request->mHull.push_back(box); + request->mHull.push_back(box); } void LLPhysicsDecomp::doDecompositionSingleHull() { - LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); - - if (decomp == NULL) - { - //stub. do nothing. - return; - } - - LLCDMeshData mesh; - - setMeshData(mesh, true); - - LLCDResult ret = decomp->buildSingleHull() ; - if (ret) - { - LL_WARNS(LOG_MESH) << "Could not execute decomposition stage when attempting to create single hull." << LL_ENDL; - make_box(mCurRequest); - } - else - { - { - LLMutexLock lock(mMutex); - mCurRequest->mHull.clear(); - mCurRequest->mHull.resize(1); - mCurRequest->mHullMesh.clear(); - } - - std::vector<LLVector3> p; - LLCDHull hull; - - // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code - decomp->getSingleHull(&hull); - - const F32* v = hull.mVertexBase; - - for (S32 j = 0; j < hull.mNumVertices; ++j) - { - LLVector3 vert(v[0], v[1], v[2]); - p.push_back(vert); - v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); - } - - { - LLMutexLock lock(mMutex); - mCurRequest->mHull[0] = p; - } - } - - { - completeCurrent(); - - } + LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); + + if (decomp == NULL) + { + //stub. do nothing. + return; + } + + LLCDMeshData mesh; + + setMeshData(mesh, true); + + LLCDResult ret = decomp->buildSingleHull() ; + if (ret) + { + LL_WARNS(LOG_MESH) << "Could not execute decomposition stage when attempting to create single hull." << LL_ENDL; + make_box(mCurRequest); + } + else + { + { + LLMutexLock lock(mMutex); + mCurRequest->mHull.clear(); + mCurRequest->mHull.resize(1); + mCurRequest->mHullMesh.clear(); + } + + std::vector<LLVector3> p; + LLCDHull hull; + + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + decomp->getSingleHull(&hull); + + const F32* v = hull.mVertexBase; + + for (S32 j = 0; j < hull.mNumVertices; ++j) + { + LLVector3 vert(v[0], v[1], v[2]); + p.push_back(vert); + v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); + } + + { + LLMutexLock lock(mMutex); + mCurRequest->mHull[0] = p; + } + } + + { + completeCurrent(); + + } } void LLPhysicsDecomp::run() { - LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); - if (decomp == NULL) - { - // stub library. Set init to true so the main thread - // doesn't wait for this to finish. - mInited = true; - return; - } - - decomp->initThread(); - mInited = true; - - static const LLCDStageData* stages = NULL; - static S32 num_stages = 0; - - if (!stages) - { - num_stages = decomp->getStages(&stages); - } - - for (S32 i = 0; i < num_stages; i++) - { - mStageID[stages[i].mName] = i; - } - - while (!mQuitting) - { - mSignal->wait(); - while (!mQuitting && !mRequestQ.empty()) - { - { - LLMutexLock lock(mMutex); - mCurRequest = mRequestQ.front(); - mRequestQ.pop(); - } - - S32& id = *(mCurRequest->mDecompID); - if (id == -1) - { - decomp->genDecomposition(id); - } - decomp->bindDecomposition(id); - - if (mCurRequest->mStage == "single_hull") - { - doDecompositionSingleHull(); - } - else - { - doDecomposition(); - } - } - } - - decomp->quitThread(); - - if (mSignal->isLocked()) - { //let go of mSignal's associated mutex - mSignal->unlock(); - } - - mDone = true; -} - -void LLPhysicsDecomp::Request::assignData(LLModel* mdl) -{ - if (!mdl) - { - return ; - } - - U16 index_offset = 0; - U16 tri[3] ; - - mPositions.clear(); - mIndices.clear(); - mBBox[1] = LLVector3(F32_MIN, F32_MIN, F32_MIN) ; - mBBox[0] = LLVector3(F32_MAX, F32_MAX, F32_MAX) ; - - //queue up vertex positions and indices - for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) - { - const LLVolumeFace& face = mdl->getVolumeFace(i); - if (mPositions.size() + face.mNumVertices > 65535) - { - continue; - } - - for (U32 j = 0; j < face.mNumVertices; ++j) - { - mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr())); - for(U32 k = 0 ; k < 3 ; k++) - { - mBBox[0].mV[k] = llmin(mBBox[0].mV[k], mPositions[j].mV[k]) ; - mBBox[1].mV[k] = llmax(mBBox[1].mV[k], mPositions[j].mV[k]) ; - } - } - - updateTriangleAreaThreshold() ; - - for (U32 j = 0; j+2 < face.mNumIndices; j += 3) - { - tri[0] = face.mIndices[j] + index_offset ; - tri[1] = face.mIndices[j + 1] + index_offset ; - tri[2] = face.mIndices[j + 2] + index_offset ; - - if(isValidTriangle(tri[0], tri[1], tri[2])) - { - mIndices.push_back(tri[0]); - mIndices.push_back(tri[1]); - mIndices.push_back(tri[2]); - } - } - - index_offset += face.mNumVertices; - } - - return ; -} - -void LLPhysicsDecomp::Request::updateTriangleAreaThreshold() -{ - F32 range = mBBox[1].mV[0] - mBBox[0].mV[0] ; - range = llmin(range, mBBox[1].mV[1] - mBBox[0].mV[1]) ; - range = llmin(range, mBBox[1].mV[2] - mBBox[0].mV[2]) ; - - mTriangleAreaThreshold = llmin(0.0002f, range * 0.000002f) ; + LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); + if (decomp == NULL) + { + // stub library. Set init to true so the main thread + // doesn't wait for this to finish. + mInited = true; + return; + } + + decomp->initThread(); + mInited = true; + + static const LLCDStageData* stages = NULL; + static S32 num_stages = 0; + + if (!stages) + { + num_stages = decomp->getStages(&stages); + } + + for (S32 i = 0; i < num_stages; i++) + { + mStageID[stages[i].mName] = i; + } + + while (!mQuitting) + { + mSignal->wait(); + while (!mQuitting && !mRequestQ.empty()) + { + { + LLMutexLock lock(mMutex); + mCurRequest = mRequestQ.front(); + mRequestQ.pop(); + } + + S32& id = *(mCurRequest->mDecompID); + if (id == -1) + { + decomp->genDecomposition(id); + } + decomp->bindDecomposition(id); + + if (mCurRequest->mStage == "single_hull") + { + doDecompositionSingleHull(); + } + else + { + doDecomposition(); + } + } + } + + decomp->quitThread(); + + if (mSignal->isLocked()) + { //let go of mSignal's associated mutex + mSignal->unlock(); + } + + mDone = true; +} + +void LLPhysicsDecomp::Request::assignData(LLModel* mdl) +{ + if (!mdl) + { + return ; + } + + U16 index_offset = 0; + U16 tri[3] ; + + mPositions.clear(); + mIndices.clear(); + mBBox[1] = LLVector3(F32_MIN, F32_MIN, F32_MIN) ; + mBBox[0] = LLVector3(F32_MAX, F32_MAX, F32_MAX) ; + + //queue up vertex positions and indices + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = mdl->getVolumeFace(i); + if (mPositions.size() + face.mNumVertices > 65535) + { + continue; + } + + for (U32 j = 0; j < face.mNumVertices; ++j) + { + mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr())); + for(U32 k = 0 ; k < 3 ; k++) + { + mBBox[0].mV[k] = llmin(mBBox[0].mV[k], mPositions[j].mV[k]) ; + mBBox[1].mV[k] = llmax(mBBox[1].mV[k], mPositions[j].mV[k]) ; + } + } + + updateTriangleAreaThreshold() ; + + for (U32 j = 0; j+2 < face.mNumIndices; j += 3) + { + tri[0] = face.mIndices[j] + index_offset ; + tri[1] = face.mIndices[j + 1] + index_offset ; + tri[2] = face.mIndices[j + 2] + index_offset ; + + if(isValidTriangle(tri[0], tri[1], tri[2])) + { + mIndices.push_back(tri[0]); + mIndices.push_back(tri[1]); + mIndices.push_back(tri[2]); + } + } + + index_offset += face.mNumVertices; + } + + return ; +} + +void LLPhysicsDecomp::Request::updateTriangleAreaThreshold() +{ + F32 range = mBBox[1].mV[0] - mBBox[0].mV[0] ; + range = llmin(range, mBBox[1].mV[1] - mBBox[0].mV[1]) ; + range = llmin(range, mBBox[1].mV[2] - mBBox[0].mV[2]) ; + + mTriangleAreaThreshold = llmin(0.0002f, range * 0.000002f) ; } //check if the triangle area is large enough to qualify for a valid triangle -bool LLPhysicsDecomp::Request::isValidTriangle(U16 idx1, U16 idx2, U16 idx3) +bool LLPhysicsDecomp::Request::isValidTriangle(U16 idx1, U16 idx2, U16 idx3) { - LLVector3 a = mPositions[idx2] - mPositions[idx1] ; - LLVector3 b = mPositions[idx3] - mPositions[idx1] ; - F32 c = a * b ; + LLVector3 a = mPositions[idx2] - mPositions[idx1] ; + LLVector3 b = mPositions[idx3] - mPositions[idx1] ; + F32 c = a * b ; - return ((a*a) * (b*b) - c * c) > mTriangleAreaThreshold ; + return ((a*a) * (b*b) - c * c) > mTriangleAreaThreshold ; } void LLPhysicsDecomp::Request::setStatusMessage(const std::string& msg) { - mStatusMessage = msg; + mStatusMessage = msg; } void LLMeshRepository::buildPhysicsMesh(LLModel::Decomposition& decomp) { - decomp.mMesh.resize(decomp.mHull.size()); - - for (U32 i = 0; i < decomp.mHull.size(); ++i) - { - LLCDHull hull; - hull.mNumVertices = decomp.mHull[i].size(); - hull.mVertexBase = decomp.mHull[i][0].mV; - hull.mVertexStrideBytes = 12; - - LLCDMeshData mesh; - LLCDResult res = LLCD_OK; - if (LLConvexDecomposition::getInstance() != NULL) - { - res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh); - } - if (res == LLCD_OK) - { - get_vertex_buffer_from_mesh(mesh, decomp.mMesh[i]); - } - } - - if (!decomp.mBaseHull.empty() && decomp.mBaseHullMesh.empty()) - { //get mesh for base hull - LLCDHull hull; - hull.mNumVertices = decomp.mBaseHull.size(); - hull.mVertexBase = decomp.mBaseHull[0].mV; - hull.mVertexStrideBytes = 12; - - LLCDMeshData mesh; - LLCDResult res = LLCD_OK; - if (LLConvexDecomposition::getInstance() != NULL) - { - res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh); - } - if (res == LLCD_OK) - { - get_vertex_buffer_from_mesh(mesh, decomp.mBaseHullMesh); - } - } + decomp.mMesh.resize(decomp.mHull.size()); + + for (U32 i = 0; i < decomp.mHull.size(); ++i) + { + LLCDHull hull; + hull.mNumVertices = decomp.mHull[i].size(); + hull.mVertexBase = decomp.mHull[i][0].mV; + hull.mVertexStrideBytes = 12; + + LLCDMeshData mesh; + LLCDResult res = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh); + } + if (res == LLCD_OK) + { + get_vertex_buffer_from_mesh(mesh, decomp.mMesh[i]); + } + } + + if (!decomp.mBaseHull.empty() && decomp.mBaseHullMesh.empty()) + { //get mesh for base hull + LLCDHull hull; + hull.mNumVertices = decomp.mBaseHull.size(); + hull.mVertexBase = decomp.mBaseHull[0].mV; + hull.mVertexStrideBytes = 12; + + LLCDMeshData mesh; + LLCDResult res = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh); + } + if (res == LLCD_OK) + { + get_vertex_buffer_from_mesh(mesh, decomp.mBaseHullMesh); + } + } } bool LLMeshRepository::meshUploadEnabled() { - LLViewerRegion *region = gAgent.getRegion(); - if(gSavedSettings.getBOOL("MeshEnabled") && - region) - { - return region->meshUploadEnabled(); - } - return false; + LLViewerRegion *region = gAgent.getRegion(); + if(gSavedSettings.getBOOL("MeshEnabled") && + region) + { + return region->meshUploadEnabled(); + } + return false; } bool LLMeshRepository::meshRezEnabled() { - LLViewerRegion *region = gAgent.getRegion(); - if(gSavedSettings.getBOOL("MeshEnabled") && - region) - { - return region->meshRezEnabled(); - } - return false; + LLViewerRegion *region = gAgent.getRegion(); + if(gSavedSettings.getBOOL("MeshEnabled") && + region) + { + return region->meshRezEnabled(); + } + return false; } // Threading: main thread only // static void LLMeshRepository::metricsStart() { - ++metrics_teleport_start_count; - sQuiescentTimer.start(0); + ++metrics_teleport_start_count; + sQuiescentTimer.start(0); } // Threading: main thread only // static void LLMeshRepository::metricsStop() { - sQuiescentTimer.stop(0); + sQuiescentTimer.stop(0); } // Threading: main thread only // static void LLMeshRepository::metricsProgress(unsigned int this_count) { - static bool first_start(true); + static bool first_start(true); - if (first_start) - { - metricsStart(); - first_start = false; - } - sQuiescentTimer.ringBell(0, this_count); + if (first_start) + { + metricsStart(); + first_start = false; + } + sQuiescentTimer.ringBell(0, this_count); } // Threading: main thread only // static void LLMeshRepository::metricsUpdate() { - F64 started, stopped; - U64 total_count(U64L(0)), user_cpu(U64L(0)), sys_cpu(U64L(0)); - - if (sQuiescentTimer.isExpired(0, started, stopped, total_count, user_cpu, sys_cpu)) - { - LLSD metrics; - - metrics["reason"] = "Mesh Download Quiescent"; - metrics["scope"] = metrics_teleport_start_count > 1 ? "Teleport" : "Login"; - metrics["start"] = started; - metrics["stop"] = stopped; - metrics["fetches"] = LLSD::Integer(total_count); - metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count); - metrics["user_cpu"] = double(user_cpu) / 1.0e6; - metrics["sys_cpu"] = double(sys_cpu) / 1.0e6; - LL_INFOS(LOG_MESH) << "EventMarker " << metrics << LL_ENDL; - } + F64 started, stopped; + U64 total_count(U64L(0)), user_cpu(U64L(0)), sys_cpu(U64L(0)); + + if (sQuiescentTimer.isExpired(0, started, stopped, total_count, user_cpu, sys_cpu)) + { + LLSD metrics; + + metrics["reason"] = "Mesh Download Quiescent"; + metrics["scope"] = metrics_teleport_start_count > 1 ? "Teleport" : "Login"; + metrics["start"] = started; + metrics["stop"] = stopped; + metrics["fetches"] = LLSD::Integer(total_count); + metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count); + metrics["user_cpu"] = double(user_cpu) / 1.0e6; + metrics["sys_cpu"] = double(sys_cpu) / 1.0e6; + LL_INFOS(LOG_MESH) << "EventMarker " << metrics << LL_ENDL; + } } // Threading: main thread only // static void teleport_started() { - LLMeshRepository::metricsStart(); + LLMeshRepository::metricsStart(); } |