diff options
author | Erik Kundiman <erik@megapahit.org> | 2025-08-16 06:39:13 +0800 |
---|---|---|
committer | Erik Kundiman <erik@megapahit.org> | 2025-08-16 06:39:13 +0800 |
commit | 2d8948ef173c5bfc37834301f4aa4fb72e78f03c (patch) | |
tree | 68b8ce2297ec590ac3f5af2e4f34736f15d7e193 /indra | |
parent | 977b636f37cd4a53caa1db084c07294f67212c0e (diff) | |
parent | 37a04baf104aa394615d8e8286522988ba56c09d (diff) |
Merge tag 'Second_Life_Release#37a04baf-2025.06' into 2025.06
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llimage/llimagebmp.cpp | 6 | ||||
-rw-r--r-- | indra/llimage/llimagedxt.cpp | 6 | ||||
-rw-r--r-- | indra/llimage/llimagejpeg.cpp | 6 | ||||
-rw-r--r-- | indra/llimagej2coj/llimagej2coj.cpp | 6 | ||||
-rw-r--r-- | indra/llkdu/llimagej2ckdu.cpp | 6 | ||||
-rw-r--r-- | indra/newview/llfloaterimagepreview.cpp | 16 | ||||
-rw-r--r-- | indra/newview/llfloatermodelpreview.cpp | 86 | ||||
-rw-r--r-- | indra/newview/llfloatermodelpreview.h | 3 | ||||
-rw-r--r-- | indra/newview/llmeshrepository.cpp | 15 | ||||
-rw-r--r-- | indra/newview/llmeshrepository.h | 8 | ||||
-rw-r--r-- | indra/newview/llviewerstats.cpp | 30 | ||||
-rw-r--r-- | indra/newview/llviewerstats.h | 9 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_stats.xml | 8 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 8 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/strings.xml | 1 |
15 files changed, 206 insertions, 8 deletions
diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp index 2a328675c2..c8f99380ea 100644 --- a/indra/llimage/llimagebmp.cpp +++ b/indra/llimage/llimagebmp.cpp @@ -558,6 +558,12 @@ bool LLImageBMP::encode(const LLImageRaw* raw_image, F32 encode_time) LL_INFOS() << "Dropping alpha information during BMP encoding" << LL_ENDL; } + if (raw_image->isBufferInvalid()) + { + setLastError("Invalid input, no buffer"); + return false; + } + setSize(raw_image->getWidth(), raw_image->getHeight(), dst_components); U8 magic[14]; diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp index 6b960f9077..c3fd0c5aa8 100644 --- a/indra/llimage/llimagedxt.cpp +++ b/indra/llimage/llimagedxt.cpp @@ -329,6 +329,12 @@ bool LLImageDXT::encodeDXT(const LLImageRaw* raw_image, F32 time, bool explicit_ { llassert_always(raw_image); + if (raw_image->isBufferInvalid()) + { + setLastError("Invalid input, no buffer"); + return false; + } + S32 ncomponents = raw_image->getComponents(); EFileFormat format; switch (ncomponents) diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp index dd3efb3459..71b139bc82 100644 --- a/indra/llimage/llimagejpeg.cpp +++ b/indra/llimage/llimagejpeg.cpp @@ -497,6 +497,12 @@ bool LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time ) resetLastError(); + if (raw_image->isBufferInvalid()) + { + setLastError("Invalid input, no buffer"); + return false; + } + LLImageDataSharedLock lockIn(raw_image); LLImageDataLock lockOut(this); diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 717b0a6e86..6ebbe359b9 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -897,6 +897,12 @@ bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible) { + if (raw_image.isBufferInvalid()) + { + base.setLastError("Invalid input, no buffer"); + return false; + } + JPEG2KEncode encode(comment_text, reversible); bool encoded = encode.encode(raw_image, base); if (!encoded) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 0d1f2b3006..b824fd8385 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -662,6 +662,12 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co bool vflip = true; bool hflip = false; + if (raw_image.isBufferInvalid()) + { + base.setLastError("Invalid input, no buffer"); + return false; + } + try { // Set up input image files diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index 42a5df5d17..550c3adc27 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -288,7 +288,9 @@ void LLFloaterImagePreview::onBtnOK() } else { - LLNotificationsUtil::add("ErrorEncodingImage"); + LLSD args; + args["REASON"] = LLImage::getLastThreadError(); + LLNotificationsUtil::add("ErrorEncodingImage", args); LL_WARNS() << "Error encoding image" << LL_ENDL; } } @@ -423,6 +425,18 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename) return false; } + // raw image is limited to 256MB so need at least some upper limit that fits into that + constexpr S32 MAX_IMAGE_AREA = 8096 * 8096; + + if (image_info.getWidth() * image_info.getHeight() > MAX_IMAGE_AREA) + { + LLStringUtil::format_map_t args; + args["PIXELS"] = llformat("%dM", (S32)(MAX_IMAGE_AREA / 1000000)); + + mImageLoadError = LLTrans::getString("texture_load_area_error", args); + return false; + } + // Load the image LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec); if (image.isNull()) diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 6a6766fb3f..08d3488ef2 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -503,7 +503,10 @@ void LLFloaterModelPreview::onClickCalculateBtn() mUploadModelUrl.clear(); mModelPhysicsFee.clear(); - gMeshRepo.uploadModel(mModelPreview->mUploadData, mModelPreview->mPreviewScale, + lod_sources_map_t lod_sources; + fillLODSourceStatistics(lod_sources); + + gMeshRepo.uploadModel(mModelPreview->mUploadData, lod_sources, mModelPreview->mPreviewScale, childGetValue("upload_textures").asBoolean(), upload_skinweights, upload_joint_positions, lock_scale_if_joint_position, mUploadModelUrl, mDestinationFolderId, false, @@ -1317,8 +1320,84 @@ void LLFloaterModelPreview::createSmoothComboBox(LLComboBox* combo_box, float mi std::string label = (++ilabel == SMOOTH_VALUES_NUMBER) ? "10 (max)" : llformat("%.1d", ilabel); combo_box->add(label, value, ADD_BOTTOM, true); } +} + +std::string get_source_file_extr(const std::string& filename) +{ + if (std::string::npos != filename.rfind(".gltf") + || std::string::npos != filename.rfind(".glb")) + { + return "gltf"; + } + else if (std::string::npos != filename.rfind(".dae")) + { + return "dae"; + } + else if (std::string::npos != filename.rfind(".slm")) + { + return "slm"; + } + else + { + return "unknown file"; + } +} +void LLFloaterModelPreview::fillLODSourceStatistics(LLFloaterModelPreview::lod_sources_map_t& lod_sources) const +{ + lod_sources.clear(); + // This doesn't nessesarily reflect the actual source of meshes, just user choices, + // some meshes could have been matched from different lods, but should be good + // enough for statistics. + for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod) + { + const std::string &lod_string = lod_name[lod]; + if (mLODMode[lod] == LLModelPreview::USE_LOD_ABOVE) + { + lod_sources[lod_string] = "lod above"; + } + else if (mLODMode[lod] == LLModelPreview::MESH_OPTIMIZER_AUTO + || mLODMode[lod] == LLModelPreview::MESH_OPTIMIZER_PRECISE + || mLODMode[lod] == LLModelPreview::MESH_OPTIMIZER_SLOPPY) + { + lod_sources[lod_string] = "generated"; + } + else if (mLODMode[lod] == LLModelPreview::LOD_FROM_FILE) + { + const std::string& file = mModelPreview->mLODFile[lod]; + lod_sources[lod_string] = get_source_file_extr(file); + } + else + { + lod_sources[lod_string] = "unknown source"; + } + } + if (mModelPreview->mLODFile[LLModel::LOD_PHYSICS].empty()) + { + if (mModelPreview->mPhysicsSearchLOD >= 0 && mModelPreview->mPhysicsSearchLOD <= 3) + { + lod_sources["physics"] = lod_name[mModelPreview->mPhysicsSearchLOD]; + } + else + { + lod_sources["physics"] = "none"; + } + } + else + { + const std::string& file = mModelPreview->mLODFile[LLModel::LOD_PHYSICS]; + if (std::string::npos == file.rfind("cube.dae")) + { + // There is a chance it will misfire if someone tries to upload a cube.dae mesh, + // but should be negligible enough. + lod_sources["physics"] = get_source_file_extr(file); + } + else + { + lod_sources["physics"] = "bounding box"; + } + } } //----------------------------------------------------------------------------- @@ -1656,7 +1735,10 @@ void LLFloaterModelPreview::onUpload(void* user_data) mp->mModelPreview->saveUploadData(upload_skinweights, upload_joint_positions, lock_scale_if_joint_position); } - gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale, + lod_sources_map_t lod_sources; + mp->fillLODSourceStatistics(lod_sources); + + gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, lod_sources, mp->mModelPreview->mPreviewScale, mp->childGetValue("upload_textures").asBoolean(), upload_skinweights, upload_joint_positions, lock_scale_if_joint_position, mp->mUploadModelUrl, mp->mDestinationFolderId, diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 7b652a3613..982f36c46e 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -223,6 +223,9 @@ private: void createSmoothComboBox(LLComboBox* combo_box, float min, float max); + typedef std::map<std::string, std::string> lod_sources_map_t; + void fillLODSourceStatistics(lod_sources_map_t& lod_sources) const; + LLUUID mDestinationFolderId; LLButton* mUploadBtn; LLButton* mCalculateBtn; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 07d68fc3ec..fd3360b234 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -2569,7 +2569,8 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_ return MESH_OK; } -LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, +LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, const LLMeshUploadThread::lod_sources_map_t& sources_list, + LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, const std::string & upload_url, LLUUID destination_folder_id, bool do_upload, LLHandle<LLWholeModelFeeObserver> fee_observer, @@ -2584,6 +2585,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mUploadObserverHandle(upload_observer) { mInstanceList = data; + mLodSources = sources_list; mUploadTextures = upload_textures; mUploadSkin = upload_skin; mUploadJoints = upload_joints; @@ -2721,6 +2723,12 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>& res["mesh_list"] = LLSD::emptyArray(); res["texture_list"] = LLSD::emptyArray(); res["instance_list"] = LLSD::emptyArray(); + LLSD& lod_sources = res["source_format"]; + lod_sources["high"] = 0; + for (auto &source : mLodSources) + { + lod_sources[source.first] = source.second; + } S32 mesh_num = 0; S32 texture_num = 0; @@ -5026,12 +5034,13 @@ bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) const return iter != mMeshHeader.end(); } -void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures, +void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, const std::map<std::string, std::string> &lod_sources, + LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, std::string upload_url, const LLUUID& destination_folder_id, bool do_upload, LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer) { - LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, + LLMeshUploadThread* thread = new LLMeshUploadThread(data, lod_sources, scale, upload_textures, upload_skin, upload_joints, lock_scale_if_joint_position, upload_url, destination_folder_id, do_upload, fee_observer, upload_observer); mUploadWaitList.push_back(thread); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 2b772f7803..ab17b921d6 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -691,6 +691,8 @@ public: }; typedef std::map<LLPointer<LLModel>, instance_list, LLUploadModelInstanceLess> instance_map; instance_map mInstance; + typedef std::map<std::string, std::string> lod_sources_map_t; + lod_sources_map_t mLodSources; LLMutex* mMutex; S32 mPendingUploads; @@ -707,7 +709,8 @@ public: std::string mWholeModelUploadURL; LLUUID mDestinationFolderId; - LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures, + LLMeshUploadThread(instance_list& data, const lod_sources_map_t& sources_list, + LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, const std::string & upload_url, const LLUUID destination_folder_id = LLUUID::null, @@ -869,7 +872,8 @@ public: bool meshUploadEnabled(); bool meshRezEnabled(); - void uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures, + void uploadModel(std::vector<LLModelInstance>& data, const std::map<std::string, std::string> &lod_sources, + LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, std::string upload_url, const LLUUID& destination_folder_id = LLUUID::null, diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index cf0cf2a6a9..59bb78f8f4 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -234,6 +234,8 @@ LLTrace::SampleStatHandle<U32> FRAMETIME_JITTER_EVENTS("frametimeevents", "Numbe FRAMETIME_JITTER_EVENTS_LAST_MINUTE("frametimeeventslastmin", "Number of frametime events in the last minute."); LLTrace::SampleStatHandle<F64> NOTRMALIZED_FRAMETIME_JITTER_SESSION("normalizedframetimejitter", "Normalized frametime jitter over the session."); +LLTrace::SampleStatHandle<F64> NFTV("nftv", "Normalized frametime variation."); +LLTrace::SampleStatHandle<F64> NORMALIZED_FRAMTIME_JITTER_PERIOD("normalizedframetimejitterperiod", "Normalized frametime jitter over the last 5 seconds."); LLTrace::EventStatHandle<LLUnit<F64, LLUnits::Meters> > AGENT_POSITION_SNAP("agentpositionsnap", "agent position corrections"); @@ -322,6 +324,8 @@ void LLViewerStats::updateFrameStats(const F64Seconds time_diff) sample(LLStatViewer::FRAMETIME_JITTER_CUMULATIVE, mTotalFrametimeJitter); sample(LLStatViewer::NOTRMALIZED_FRAMETIME_JITTER_SESSION, mTotalFrametimeJitter / mTotalTime); + mLastNoramlizedSessionJitter = mTotalFrametimeJitter / mTotalTime; + static LLCachedControl<F32> frameTimeEventThreshold(gSavedSettings, "StatsFrametimeEventThreshold", 0.1f); if (time_diff - mLastTimeDiff > mLastTimeDiff * frameTimeEventThreshold()) @@ -360,6 +364,27 @@ void LLViewerStats::updateFrameStats(const F64Seconds time_diff) sample(LLStatViewer::FRAMETIME_JITTER_99TH, ninety_ninth_percentile); sample(LLStatViewer::FRAMETIME_JITTER_95TH, ninety_fifth_percentile); + F64 averageFrameTime = 0; + for (const auto& frame_time : mFrameTimes) + { + averageFrameTime += frame_time.value(); + } + averageFrameTime /= mFrameTimes.size(); + + sample(LLStatViewer::NFTV, frame_time_stddev / averageFrameTime); + mLastNormalizedFrametimeVariance = frame_time_stddev / averageFrameTime; + + // Add up all of the jitter values. + F64 totalJitter = 0; + for (const auto& frame_jitter : mFrameTimesJitter) + { + totalJitter += frame_jitter.value(); + } + + mLastNormalizedPeriodJitter = totalJitter / mLastFrameTimeSample; + + sample(LLStatViewer::NORMALIZED_FRAMTIME_JITTER_PERIOD, mLastNormalizedPeriodJitter); + mFrameTimes.clear(); mFrameTimesJitter.clear(); mLastFrameTimeSample = F64Seconds(0); @@ -648,6 +673,11 @@ void send_viewer_stats(bool include_preferences) // send fps only for time app spends in foreground agent["fps"] = (F32)gForegroundFrameCount / gForegroundTime.getElapsedTimeF32(); + + agent["normalized_session_jitter"] = LLViewerStats::instance().getLastNormalizedSessionJitter(); + agent["normalized_frametime_variance"] = LLViewerStats::instance().getLastNormalizedFrametimeVariance(); + agent["normalized_period_jitter"] = LLViewerStats::instance().getLastNormalizedPeriodJitter(); + agent["version"] = LLVersionInfo::instance().getChannelAndVersion(); std::string language = LLUI::getLanguage(); agent["language"] = language; diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index 63fb7d4a17..011269d7ee 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -271,6 +271,10 @@ public: LLTrace::Recording& getRecording() { return mRecording; } const LLTrace::Recording& getRecording() const { return mRecording; } + F64 getLastNormalizedSessionJitter() const { return mLastNoramlizedSessionJitter; } + F64 getLastNormalizedFrametimeVariance() const { return mLastNormalizedFrametimeVariance; } + F64 getLastNormalizedPeriodJitter() const { return mLastNormalizedPeriodJitter; } + private: LLTrace::Recording mRecording; @@ -286,6 +290,11 @@ private: F64Seconds mTimeSinceLastEventSample; std::vector<F64Seconds> mFrameTimes; // used for frame time stats std::vector<F64Seconds> mFrameTimesJitter; // used for frame time jitter stats + + F64 mLastNoramlizedSessionJitter; // used for frame time jitter stats + F64 mLastNormalizedFrametimeVariance; // Used when submitting jitter stats + F64 mLastNormalizedPeriodJitter; + }; static const F32 SEND_STATS_PERIOD = 300.0f; diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml index f2309eb817..1600c422c3 100644 --- a/indra/newview/skins/default/xui/en/floater_stats.xml +++ b/indra/newview/skins/default/xui/en/floater_stats.xml @@ -58,6 +58,14 @@ label="normalized sess. jitter" decimal_digits="4" stat="normalizedframetimejitter"/> + <stat_bar name="normalized_period_jitter" + label="normalized period jitter" + decimal_digits="4" + stat="normalizedframetimejitterperiod"/> + <stat_bar name="normalized_frametime_variation" + label="normalized frametime variation" + decimal_digits="4" + stat="nftv"/> <stat_bar name="frame_events_per_minute" label="frame events/minute" decimal_digits="2" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 10c9efffd1..9963ccd84b 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -1322,6 +1322,14 @@ Error encoding snapshot. <notification icon="alertmodal.tga" + name="ErrorEncodingImage" + type="alertmodal"> + Failed to encode image, reason: [REASON] + <tag>fail</tag> + </notification> + + <notification + icon="alertmodal.tga" name="ErrorTextureCannotAfford" type="alertmodal"> You need L$[COST] to save a texture to your inventory. You may either buy L$ or save the photo to your computer instead. diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index ccae7d3df1..25827fa236 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3971,6 +3971,7 @@ Abuse Report</string> <string name="AvatarBirthDateFormatShort">[mthnum,datetime,slt]/[day,datetime,slt]</string> <string name="DefaultMimeType">none/none</string> + <string name="texture_load_area_error">Can't load images larger than [PIXELS] pixels</string> <string name="texture_load_dimensions_error">Can't load images larger than [WIDTH]*[HEIGHT]</string> <string name="texture_load_format_error">Incorrect image format.</string> <string name="texture_load_empty_file">File is empty.</string> |