From 576c558759aa84df7b30ee29ca55143719d73028 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 17 Sep 2024 20:03:03 +0300 Subject: Xcode16 build fix --- .github/workflows/build.yaml | 4 ++-- indra/CMakeLists.txt | 3 +++ indra/llcommon/llqueuedthread.cpp | 4 ++-- indra/llrender/llimagegl.cpp | 2 +- indra/llwindow/llwindowmacosx.cpp | 8 ++++---- indra/newview/gltfscenemanager.cpp | 2 +- indra/newview/llmeshrepository.cpp | 6 +++--- indra/newview/llmeshrepository.h | 4 ++-- indra/newview/llpanelemojicomplete.cpp | 2 +- indra/newview/lltexturefetch.cpp | 6 +++--- indra/newview/llviewermedia.cpp | 4 ++-- indra/newview/llviewerwindow.cpp | 2 +- indra/newview/llvoicewebrtc.cpp | 20 ++++++++++---------- indra/viewer_components/login/lllogin.cpp | 3 +-- 14 files changed, 36 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2cb94c9c90..8103d5c039 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,7 +42,7 @@ jobs: needs: setup strategy: matrix: - runner: [windows-large, macos-12-xl] + runner: [windows-large, macos-14-xlarge] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} runs-on: ${{ matrix.runner }} outputs: @@ -64,7 +64,7 @@ jobs: # autobuild-package.xml. AUTOBUILD_VCS_INFO: "true" AUTOBUILD_VSVER: "170" - DEVELOPER_DIR: "/Applications/Xcode_14.0.1.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode_16.0.app/Contents/Developer" # Ensure that Linden viewer builds engage Bugsplat. BUGSPLAT_DB: ${{ needs.setup.outputs.bugsplat_db }} build_coverity: false diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 422927704a..bf0a051799 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -29,6 +29,9 @@ else() set( USE_AUTOBUILD_3P ON ) endif() +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + include(Variables) include(BuildVersion) diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index 1c4ac5a7bf..0196a24b18 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -146,7 +146,7 @@ size_t LLQueuedThread::updateQueue(F32 max_time_ms) // schedule a call to threadedUpdate for every call to updateQueue if (!isQuitting()) { - mRequestQueue.post([=]() + mRequestQueue.post([=, this]() { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update"); mIdleThread = false; @@ -474,7 +474,7 @@ void LLQueuedThread::processRequest(LLQueuedThread::QueuedRequest* req) #else using namespace std::chrono_literals; auto retry_time = LL::WorkQueue::TimePoint::clock::now() + 16ms; - mRequestQueue.post([=] + mRequestQueue.post([=, this] { LL_PROFILE_ZONE_NAMED("processRequest - retry"); if (LL::WorkQueue::TimePoint::clock::now() < retry_time) diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 67b4ada62f..ba77c6f1a4 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1777,7 +1777,7 @@ void LLImageGL::syncToMainThread(LLGLuint new_tex_name) ref(); LL::WorkQueue::postMaybe( mMainQueue, - [=]() + [=, this]() { LL_PROFILE_ZONE_NAMED("cglt - delete callback"); syncTexName(new_tex_name); diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 80001b14ee..cc88268c67 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1040,7 +1040,7 @@ F32 LLWindowMacOSX::getGamma() &greenGamma, &blueMin, &blueMax, - &blueGamma) == noErr) + &blueGamma) == static_cast(noErr)) { // So many choices... // Let's just return the green channel gamma for now. @@ -1091,7 +1091,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma) &greenGamma, &blueMin, &blueMax, - &blueGamma) != noErr) + &blueGamma) != static_cast(noErr)) { return false; } @@ -1106,7 +1106,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma) gamma, blueMin, blueMax, - gamma) != noErr) + gamma) != static_cast(noErr)) { return false; } @@ -1158,7 +1158,7 @@ bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position) newPosition.y = screen_pos.mY; CGSetLocalEventsSuppressionInterval(0.0); - if(CGWarpMouseCursorPosition(newPosition) == noErr) + if(CGWarpMouseCursorPosition(newPosition) == static_cast(noErr)) { result = true; } diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index ce54fa4c12..f043277367 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -497,7 +497,7 @@ void GLTFSceneManager::update() LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response) { LLAppViewer::instance()->postToMainCoro( - [=]() + [=, this]() { if (mUploadingAsset) { diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 1f214344ff..ebfbc435ae 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -548,8 +548,8 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate return ppTex ? (*ppTex).get() : NULL; } -volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; -volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; +std::atomic LLMeshRepoThread::sActiveHeaderRequests = 0; +std::atomic LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN; S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; @@ -3914,7 +3914,7 @@ void LLMeshRepository::notifyLoadedMeshes() } // erase from background thread - mThread->mWorkQueue.post([=]() + mThread->mWorkQueue.post([=, this]() { mThread->mSkinMap.erase(id); }); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index b5c2424578..3d25a4dd78 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -256,8 +256,8 @@ class LLMeshRepoThread : public LLThread { public: - volatile static S32 sActiveHeaderRequests; - volatile static S32 sActiveLODRequests; + static std::atomic sActiveHeaderRequests; + static std::atomic sActiveLODRequests; static U32 sMaxConcurrentRequests; static S32 sRequestLowWater; static S32 sRequestHighWater; diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index cb89a5910e..fc1bf6ca93 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -438,7 +438,7 @@ void LLPanelEmojiComplete::updateConstraints() { mRenderRect = getLocalRect(); - mEmojiWidth = (U16)(mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2); + mEmojiWidth = (U16)(mIconFont->getWidthF32(LLWString(1, 0x1F431).c_str()) + mPadding * 2); if (mVertical) { mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 69c40066b4..ee98728ff1 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2832,7 +2832,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { LL_PROFILE_ZONE_SCOPED; - mRequestQueue.tryPost([=]() + mRequestQueue.tryPost([=, this]() { LLTextureFetchWorker* worker = getWorker(id); if (worker) @@ -3523,8 +3523,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) //if (! gViewerAssetStatsThread1) // return true; - static volatile bool reporting_started(false); - static volatile S32 report_sequence(0); + static std::atomic reporting_started(false); + static std::atomic report_sequence(0); // In mStatsSD, we have a copy we own of the LLSD representation // of the asset stats. Add some additional fields and ship it off. diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 9739cac311..a29c6afe66 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2900,14 +2900,14 @@ void LLViewerMediaImpl::update() media_tex->ref(); main_queue->postTo( mTexUpdateQueue, // Worker thread queue - [=]() // work done on update worker thread + [=, this]() // work done on update worker thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); #endif doMediaTexUpdate(media_tex, data, data_width, data_height, x_pos, y_pos, width, height, true); }, - [=]() // callback to main thread + [=, this]() // callback to main thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 0e7d82fd90..d35a504228 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1331,7 +1331,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi // Check the whitelist, if there's media (otherwise just show it) if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) { - if ( obj != mDragHoveredObject) + if (obj != mDragHoveredObject.get()) { // Highlight the dragged object LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 3d684e5a1b..9c0bdf0e18 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -549,7 +549,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } } LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (sShuttingDown) { return; @@ -667,7 +667,7 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi { LL::WorkQueue::postMaybe(mMainQueue, - [=] + [=, this] { OnDevicesChangedImpl(render_devices, capture_devices); }); @@ -2203,7 +2203,7 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; switch (state) @@ -2226,7 +2226,7 @@ void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObs // callback from llwebrtc void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate& candidate) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { mIceCandidates.push_back(candidate); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { mIceCandidates.push_back(candidate); }); } void LLVoiceWebRTCConnection::processIceUpdates() @@ -2344,7 +2344,7 @@ void LLVoiceWebRTCConnection::processIceUpdatesCoro(connectionPtr_t connection) void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2371,7 +2371,7 @@ void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface* audio_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2393,7 +2393,7 @@ void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterfac void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL; if (!mShutDown) { @@ -2407,7 +2407,7 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded() void LLVoiceWebRTCConnection::OnPeerConnectionClosed() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Peer connection has closed." << LL_ENDL; if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_CLOSE) { @@ -2879,7 +2879,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() // llwebrtc callback void LLVoiceWebRTCConnection::OnDataReceived(const std::string& data, bool binary) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); } // @@ -3035,7 +3035,7 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index feebecf4cb..a6c4afd6c4 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -127,8 +127,7 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params) // Launch a coroutine with our login_() method. Run the coroutine until // its first wait; at that point, return here. - std::string coroname = - LLCoros::instance().launch("LLLogin::Impl::login_", [=]() { loginCoro(uri, login_params); }); + std::string coroname = LLCoros::instance().launch("LLLogin::Impl::login_", [=, this]() { loginCoro(uri, login_params); }); LL_DEBUGS("LLLogin") << " connected with uri '" << uri << "', login_params " << login_params << LL_ENDL; } -- cgit v1.2.3 From 705fd68ed7cdc6db39ddd237b7f2c70045fc01fb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 24 Sep 2024 12:53:33 -0400 Subject: Make `LLPointer` equality comparisons accept not-identical types. That is, both `LLPointer::operator==()` and `operator!=()` and the free function `operator==()` and `operator!=()` now accept pointer types other than the type of the subject `LLPointer`, letting the compiler apply implicit pointer conversions or diagnose an error. --- indra/llcommon/llpointer.h | 23 ++++++++++++++--------- indra/newview/llviewerwindow.cpp | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index 048547e4cc..a886d6538e 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -105,11 +105,16 @@ public: bool notNull() const { return (mPointer != nullptr); } operator Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLPointer& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLPointer& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLPointer& ptr) const { return (mPointer > ptr.mPointer); } + template + bool operator !=(Type1* ptr) const { return (mPointer != ptr); } + template + bool operator ==(Type1* ptr) const { return (mPointer == ptr); } + template + bool operator !=(const LLPointer& ptr) const { return (mPointer != ptr.mPointer); } + template + bool operator ==(const LLPointer& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLPointer& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLPointer& ptr) const { return (mPointer > ptr.mPointer); } LLPointer& operator =(Type* ptr) { @@ -418,14 +423,14 @@ private: bool mStayUnique; }; -template -bool operator!=(Type* lhs, const LLPointer& rhs) +template +bool operator!=(Type0* lhs, const LLPointer& rhs) { return (lhs != rhs.get()); } -template -bool operator==(Type* lhs, const LLPointer& rhs) +template +bool operator==(Type0* lhs, const LLPointer& rhs) { return (lhs == rhs.get()); } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index d35a504228..7f45e109cb 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1331,7 +1331,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi // Check the whitelist, if there's media (otherwise just show it) if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) { - if (obj != mDragHoveredObject.get()) + if (obj != mDragHoveredObject) { // Highlight the dragged object LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); -- cgit v1.2.3 From 1a682de4e42dcea932d25ffe980a2dfec345211e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 24 Sep 2024 13:41:02 -0400 Subject: LLConstPointer is the same as LLPointer. Instead of restating the whole class, requiring changes to be made in parallel (which has already failed), just make a template alias. --- indra/llcommon/llpointer.h | 166 +-------------------------------------------- 1 file changed, 2 insertions(+), 164 deletions(-) diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index a886d6538e..bbe3595435 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -210,170 +210,8 @@ protected: Type* mPointer; }; -template class LLConstPointer -{ - template - friend class LLConstPointer; -public: - LLConstPointer() : - mPointer(nullptr) - { - } - - LLConstPointer(const Type* ptr) : - mPointer(ptr) - { - ref(); - } - - LLConstPointer(const LLConstPointer& ptr) : - mPointer(ptr.mPointer) - { - ref(); - } - - LLConstPointer(LLConstPointer&& ptr) noexcept - { - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template - LLConstPointer(const LLConstPointer& ptr) : - mPointer(ptr.get()) - { - ref(); - } - - template - LLConstPointer(LLConstPointer&& ptr) noexcept : - mPointer(ptr.get()) - { - ptr.mPointer = nullptr; - } - - ~LLConstPointer() - { - unref(); - } - - const Type* get() const { return mPointer; } - const Type* operator->() const { return mPointer; } - const Type& operator*() const { return *mPointer; } - - operator BOOL() const { return (mPointer != nullptr); } - operator bool() const { return (mPointer != nullptr); } - bool operator!() const { return (mPointer == nullptr); } - bool isNull() const { return (mPointer == nullptr); } - bool notNull() const { return (mPointer != nullptr); } - - operator const Type*() const { return mPointer; } - bool operator !=(const Type* ptr) const { return (mPointer != ptr); } - bool operator ==(const Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLConstPointer& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLConstPointer& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLConstPointer& ptr) const { return (mPointer > ptr.mPointer); } - - LLConstPointer& operator =(const Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - - return *this; - } - - LLConstPointer& operator =(const LLConstPointer& ptr) - { - if( mPointer != ptr.mPointer ) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - return *this; - } - - LLConstPointer& operator =(LLConstPointer&& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template - LLConstPointer& operator =(const LLConstPointer& ptr) - { - if( mPointer != ptr.get() ) - { - unref(); - mPointer = ptr.get(); - ref(); - } - return *this; - } - - template - LLConstPointer& operator =(LLConstPointer&& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - return *this; - } - - // Just exchange the pointers, which will not change the reference counts. - static void swap(LLConstPointer& a, LLConstPointer& b) - { - const Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; - } - -protected: -#ifdef LL_LIBRARY_INCLUDE - void ref(); - void unref(); -#else // LL_LIBRARY_INCLUDE - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - const Type *temp = mPointer; - mPointer = nullptr; - temp->unref(); - if (mPointer != nullptr) - { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; - unref(); - } - } - } -#endif // LL_LIBRARY_INCLUDE - -protected: - const Type* mPointer; -}; +template +using LLConstPointer = LLPointer; template class LLCopyOnWritePointer : public LLPointer -- cgit v1.2.3 From 98c829a47cd2b38d51602cce3de873179a394ba7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 25 Sep 2024 09:43:43 -0400 Subject: Use copy-and-swap idiom for LLPointer's assignment operators. This affords strong exception safety. Also, eliminating the conditional may improve speed. --- indra/llcommon/llpointer.h | 53 ++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index bbe3595435..c5dd5532cc 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -28,6 +28,7 @@ #include "llerror.h" // *TODO: consider eliminating this #include "llmutex.h" +#include // std::swap() //---------------------------------------------------------------------------- // RefCount objects should generally only be accessed by way of LLPointer<>'s @@ -118,24 +119,26 @@ public: LLPointer& operator =(Type* ptr) { - assign(ptr); + // copy-and-swap idiom, see http://gotw.ca/gotw/059.htm + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } LLPointer& operator =(const LLPointer& ptr) { - assign(ptr); + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } LLPointer& operator =(LLPointer&& ptr) { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } + LLPointer temp(std::move(ptr)); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } @@ -143,28 +146,32 @@ public: template LLPointer& operator =(const LLPointer& ptr) { - assign(ptr.get()); + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } template LLPointer& operator =(LLPointer&& ptr) { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } + LLPointer temp(std::move(ptr)); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } // Just exchange the pointers, which will not change the reference counts. static void swap(LLPointer& a, LLPointer& b) { - Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; + using std::swap; // per Swappable convention + swap(a.mPointer, b.mPointer); + } + + // Put swap() overload in the global namespace, per Swappable convention + friend void swap(LLPointer& a, LLPointer& b) + { + LLPointer::swap(a, b); } protected: @@ -196,16 +203,6 @@ protected: } #endif // LL_LIBRARY_INCLUDE - void assign(const LLPointer& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - } - protected: Type* mPointer; }; -- cgit v1.2.3 From 5d7b3abe7759545b5ff12dc8ae5a6de9ed258953 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 25 Sep 2024 10:15:20 -0400 Subject: Explain why apparently redundant LLPointer methods are necessary. Given templated constructors and assignment operators, it's tempting to remove specific constructors and assignment operators with the same LLPointer parameters. That turns out to be a mistake. Add comments to warn future maintainers. --- indra/llcommon/llpointer.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index c5dd5532cc..71c955c4c5 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -61,6 +61,13 @@ public: ref(); } + // Even though the template constructors below accepting + // (const LLPointer&) and (LLPointer&&) appear to + // subsume these specific (const LLPointer&) and (LLPointer&&) + // constructors, the compiler recognizes these as The Copy Constructor and + // The Move Constructor, respectively. In other words, even in the + // presence of the LLPointer constructors, we still must specify + // the LLPointer constructors. LLPointer(const LLPointer& ptr) : mPointer(ptr.mPointer) { @@ -126,6 +133,13 @@ public: return *this; } + // Even though the template assignment operators below accepting + // (const LLPointer&) and (LLPointer&&) appear to + // subsume these specific (const LLPointer&) and (LLPointer&&) + // assignment operators, the compiler recognizes these as Copy Assignment + // and Move Assignment, respectively. In other words, even in the presence + // of the LLPointer assignment operators, we still must specify + // the LLPointer operators. LLPointer& operator =(const LLPointer& ptr) { LLPointer temp(ptr); -- cgit v1.2.3 From dc0c6d396eb70392f294eea65975646dad662f6a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 25 Sep 2024 21:38:46 -0400 Subject: Adapt `fsyspath` for C++20 conventions. In C++20, `std::filesystem::u8path()` (that accepts a UTF-8 encoded `std::string` and returns a `std::filesystem::path`) is deprecated. Instead, to engage UTF-8 coding conversions, we're supposed to pass the `path` constructor a `std::u8string`, i.e. a `std::basic_string`. Since `char8_t` is a type distinct from both `char` and `unsigned char`, we must Do Something to pass a UTF-8 encoded `std::string` into `std::filesystem::path`. To avoid copying characters from a `std::string` into a temporary `std::u8string` and from there into the `std::filesystem::path`, make a `boost::transform_iterator` that accepts a `std::string_view::iterator` and adapts it to dereference `char8_t` characters. Make `fsyspath(std::string_view)` engage the base-class constructor accepting (iterator, iterator), adapting `string_view::begin()` and `end()` to deliver `char8_t` characters. Use the same tactic for `fsyspath::operator=(std::string_view)`, explicitly calling `std::filesystem::path::assign()` with the adapted iterators. To resolve ambiguities, provide both constructors and assignment operators accepting `(const std::string&)` and `(const char*)`, explicitly converting each to `std::string_view`. At the same time, `std::filesystem::path::u8string()` now returns `std::u8string` rather than `std::string`. Since `std::filesystem::path` delivers only that `std::u8string` rather than iterators into its internal representation, we can't avoid capturing it and copying to the returned `std::string`. Remove explicit `.u8string()` calls from a few existing `fsyspath` instances, now that `fsyspath` supports implicit conversion to `std::string`. --- indra/llcommon/fsyspath.h | 56 ++++++++++++++++++++++++++--------------- indra/llcommon/lua_function.cpp | 8 +++--- indra/llui/llluafloater.cpp | 4 +-- indra/newview/llluamanager.cpp | 8 +++--- 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h index 1b4aec09b4..f66970ed8f 100644 --- a/indra/llcommon/fsyspath.h +++ b/indra/llcommon/fsyspath.h @@ -12,7 +12,10 @@ #if ! defined(LL_FSYSPATH_H) #define LL_FSYSPATH_H +#include #include +#include +#include // While std::filesystem::path can be directly constructed from std::string on // both Posix and Windows, that's not what we want on Windows. Per @@ -33,42 +36,55 @@ // char"), the "native narrow encoding" isn't UTF-8, so file paths containing // non-ASCII characters get mangled. // -// Once we're building with C++20, we could pass a UTF-8 std::string through a -// vector to engage std::filesystem::path's own UTF-8 conversion. But -// sigh, as of 2024-04-03 we're not yet there. -// -// Anyway, encapsulating the important UTF-8 conversions in our own subclass -// allows us to migrate forward to C++20 conventions without changing -// referencing code. +// Encapsulating the important UTF-8 conversions in our own subclass allows us +// to migrate forward to C++20 conventions without changing referencing code. class fsyspath: public std::filesystem::path { using super = std::filesystem::path; + // In C++20 (__cpp_lib_char8_t), std::filesystem::u8path() is deprecated. + // std::filesystem::path(iter, iter) performs UTF-8 conversions when the + // value_type of the iterators is char8_t. While we could copy into a + // temporary std::u8string and from there into std::filesystem::path, to + // minimize string copying we'll define a transform_iterator that accepts + // a std::string_view::iterator and dereferences to char8_t. + struct u8ify + { + char8_t operator()(char c) const { return char8_t(c); } + }; + using u8iter = boost::transform_iterator; + public: // default fsyspath() {} - // construct from UTF-8 encoded std::string - fsyspath(const std::string& path): super(std::filesystem::u8path(path)) {} - // construct from UTF-8 encoded const char* - fsyspath(const char* path): super(std::filesystem::u8path(path)) {} + // construct from UTF-8 encoded string + fsyspath(const std::string& path): fsyspath(std::string_view(path)) {} + fsyspath(const char* path): fsyspath(std::string_view(path)) {} + fsyspath(std::string_view path): + super(u8iter(path.begin(), u8ify()), u8iter(path.end(), u8ify())) + {} // construct from existing path fsyspath(const super& path): super(path) {} - fsyspath& operator=(const super& p) { super::operator=(p); return *this; } - fsyspath& operator=(const std::string& p) - { - super::operator=(std::filesystem::u8path(p)); - return *this; - } - fsyspath& operator=(const char* p) + fsyspath& operator=(const super& p) { super::operator=(p); return *this; } + fsyspath& operator=(const std::string& p) { return (*this) = std::string_view(p); } + fsyspath& operator=(const char* p) { return (*this) = std::string_view(p); } + fsyspath& operator=(std::string_view p) { - super::operator=(std::filesystem::u8path(p)); + assign(u8iter(p.begin(), u8ify()), u8iter(p.end(), u8ify())); return *this; } // shadow base-class string() method with UTF-8 aware method - std::string string() const { return super::u8string(); } + std::string string() const + { + // Short of forbidden type punning, I see no way to avoid copying this + // std::u8string to a std::string. + auto u8str{ super::u8string() }; + // from https://github.com/tahonermann/char8_t-remediation/blob/master/char8_t-remediation.h#L180-L182 + return { u8str.begin(), u8str.end() }; + } // On Posix systems, where value_type is already char, this operator // std::string() method shadows the base class operator string_type() // method. But on Windows, where value_type is wchar_t, the base class diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index a9f88f3170..e28c0caa27 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -170,7 +170,7 @@ fsyspath source_path(lua_State* L) { lua_getinfo(L, i, "s", &ar); } - return ar.source; + return { ar.source }; } } // namespace lluau @@ -1108,7 +1108,7 @@ lua_function(source_path, "source_path(): return the source path of the running { lua_checkdelta(L, 1); lluau_checkstack(L, 1); - lua_pushstdstring(L, lluau::source_path(L).u8string()); + lua_pushstdstring(L, lluau::source_path(L)); return 1; } @@ -1119,7 +1119,7 @@ lua_function(source_dir, "source_dir(): return the source directory of the runni { lua_checkdelta(L, 1); lluau_checkstack(L, 1); - lua_pushstdstring(L, lluau::source_path(L).parent_path().u8string()); + lua_pushstdstring(L, lluau::source_path(L).parent_path()); return 1; } @@ -1132,7 +1132,7 @@ lua_function(abspath, "abspath(path): " lua_checkdelta(L); auto path{ lua_tostdstring(L, 1) }; lua_pop(L, 1); - lua_pushstdstring(L, (lluau::source_path(L).parent_path() / path).u8string()); + lua_pushstdstring(L, (lluau::source_path(L).parent_path() / path)); return 1; } diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index ccdadc6ae0..83e4c9c983 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -301,11 +301,11 @@ void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) void LLLuaFloater::showLuaFloater(const LLSD &data) { fsyspath fs_path(data["xml_path"].asString()); - std::string path = fs_path.lexically_normal().u8string(); + std::string path = fs_path.lexically_normal(); if (!fs_path.is_absolute()) { std::string lib_path = gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"); - path = (fsyspath(lib_path) / path).u8string(); + path = fsyspath(fsyspath(lib_path) / path); } LLLuaFloater *floater = new LLLuaFloater(data); diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 7fe5c1ece0..322717fbb9 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -317,7 +317,7 @@ LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : void LLRequireResolver::findModule() { // If mPathToResolve is absolute, this replaces mSourceDir. - auto absolutePath = (mSourceDir / mPathToResolve).u8string(); + fsyspath absolutePath(mSourceDir / mPathToResolve); // Push _MODULES table on stack for checking and saving to the cache luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1); @@ -333,7 +333,7 @@ void LLRequireResolver::findModule() // not already cached - prep error message just in case auto fail{ - [L=L, path=mPathToResolve.u8string()]() + [L=L, path=mPathToResolve.string()]() { luaL_error(L, "could not find require('%s')", path.data()); }}; if (mPathToResolve.is_absolute()) @@ -348,10 +348,10 @@ void LLRequireResolver::findModule() { // if path is already absolute, operator/() preserves it auto abspath(fsyspath(gDirUtilp->getAppRODataDir()) / path.asString()); - std::string absolutePathOpt = (abspath / mPathToResolve).u8string(); + fsyspath absolutePathOpt = (abspath / mPathToResolve); if (absolutePathOpt.empty()) - luaL_error(L, "error requiring module '%s'", mPathToResolve.u8string().data()); + luaL_error(L, "error requiring module '%s'", mPathToResolve.string().data()); if (findModuleImpl(absolutePathOpt)) return; -- cgit v1.2.3 From 988a0fdd1eedab24677ed517942a4ef20c830f08 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 26 Sep 2024 09:00:48 -0400 Subject: Fix a few more fsyspath conversions, removing explicit u8string(). --- indra/llcommon/lua_function.cpp | 4 ++-- indra/llui/llluafloater.cpp | 2 +- indra/newview/tests/llluamanager_test.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index e28c0caa27..de06dab264 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -1119,7 +1119,7 @@ lua_function(source_dir, "source_dir(): return the source directory of the runni { lua_checkdelta(L, 1); lluau_checkstack(L, 1); - lua_pushstdstring(L, lluau::source_path(L).parent_path()); + lua_pushstdstring(L, fsyspath(lluau::source_path(L).parent_path())); return 1; } @@ -1132,7 +1132,7 @@ lua_function(abspath, "abspath(path): " lua_checkdelta(L); auto path{ lua_tostdstring(L, 1) }; lua_pop(L, 1); - lua_pushstdstring(L, (lluau::source_path(L).parent_path() / path)); + lua_pushstdstring(L, fsyspath(lluau::source_path(L).parent_path() / path)); return 1; } diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index 83e4c9c983..91c0cfeec9 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -301,7 +301,7 @@ void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) void LLLuaFloater::showLuaFloater(const LLSD &data) { fsyspath fs_path(data["xml_path"].asString()); - std::string path = fs_path.lexically_normal(); + fsyspath path = fs_path.lexically_normal(); if (!fs_path.is_absolute()) { std::string lib_path = gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"); diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index f0a1b32eed..9687b68451 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -55,14 +55,14 @@ namespace tut // indra/newview/tests/llluamanager_test.cpp => // indra/newview auto newview{ fsyspath(__FILE__).parent_path().parent_path() }; - auto settings{ newview / "app_settings" / "settings.xml" }; + fsyspath settings{ newview / "app_settings" / "settings.xml" }; // true suppresses implicit declare; implicit declare requires // that every variable in settings.xml has a Comment, which many don't. - gSavedSettings.loadFromFile(settings.u8string(), true); + gSavedSettings.loadFromFile(settings, true); // At test time, since we don't have the app bundle available, // extend LuaRequirePath to include the require directory in the // source tree. - auto require{ (newview / "scripts" / "lua" / "require").u8string() }; + std::string require{ fsyspath(newview / "scripts" / "lua" / "require") }; auto paths{ gSavedSettings.getLLSD("LuaRequirePath") }; bool found = false; for (const auto& path : llsd::inArray(paths)) -- cgit v1.2.3 From 015ff3d6083a6ea6f7d9e30b22cfe080c81c04a5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 26 Sep 2024 09:22:09 -0400 Subject: #include rather than "math.h" to avoid lerp() conflict. GCC on Linux complains that "math.h", which hoists all the standard library math functions into the global namespace for classic C compatibility, creates a conflict between `std::lerp()` and the `lerp()` function in llmath.h. (Perhaps we should just replace our `lerp()` definition with `using std::lerp;`) Anyway, bringing in rather than "math.h" leaves standard library math functions in the `std` namespace, avoiding the conflict. --- indra/llmath/raytrace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmath/raytrace.cpp b/indra/llmath/raytrace.cpp index 893bf1fc70..1251ab9f8d 100644 --- a/indra/llmath/raytrace.cpp +++ b/indra/llmath/raytrace.cpp @@ -26,7 +26,7 @@ #include "linden_common.h" -#include "math.h" +#include //#include "vmath.h" #include "v3math.h" #include "llquaternion.h" -- cgit v1.2.3 From 6400284278071b774d2837d62142a15b94120198 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 26 Sep 2024 09:42:14 -0400 Subject: Ditch our own (conflicting) definition of the lerp() function. Hoist `std::lerp()` into the global namespace instead. --- indra/llmath/llmath.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index fa315291a3..09b685a68b 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -358,10 +358,9 @@ inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs) return new_foo; } -inline F32 lerp(F32 a, F32 b, F32 u) -{ - return a + ((b - a) * u); -} +// We used to define a simple lerp(F32, F32, F32) that probably predated +// std::lerp(). By now, do we need our own definition any longer? +using std::lerp; inline F32 lerp2d(F32 x00, F32 x01, F32 x10, F32 x11, F32 u, F32 v) { -- cgit v1.2.3 From 6ac59d1d5e200cccd3ddaa6d1c3b0d8d116a06be Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 26 Sep 2024 10:30:40 -0400 Subject: Fix GCC ambiguous-reversed-operator errors for `LLKeyData` compares. `LLKeyData::operator==(const LLKeyData&)` and `operator!=(const LLKeyData&)` were not themselves `const` methods. In C++20, that can produce a fatal warning that if the compare operands were reversed, you'd get different results. Making them both `const` should fix it. While touching the method definitions, make `operator==()` more intuitive, and make `operator!=()` simply negate `operator==()` instead of restating it in reverse. --- indra/llcommon/llkeybind.cpp | 20 ++++++++------------ indra/llcommon/llkeybind.h | 4 ++-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp index e36c1d0a4c..34254f3ef5 100644 --- a/indra/llcommon/llkeybind.cpp +++ b/indra/llcommon/llkeybind.cpp @@ -123,22 +123,18 @@ LLKeyData& LLKeyData::operator=(const LLKeyData& rhs) return *this; } -bool LLKeyData::operator==(const LLKeyData& rhs) +bool LLKeyData::operator==(const LLKeyData& rhs) const { - if (mMouse != rhs.mMouse) return false; - if (mKey != rhs.mKey) return false; - if (mMask != rhs.mMask) return false; - if (mIgnoreMasks != rhs.mIgnoreMasks) return false; - return true; + return + (mMouse == rhs.mMouse) && + (mKey == rhs.mKey) && + (mMask == rhs.mMask) && + (mIgnoreMasks == rhs.mIgnoreMasks); } -bool LLKeyData::operator!=(const LLKeyData& rhs) +bool LLKeyData::operator!=(const LLKeyData& rhs) const { - if (mMouse != rhs.mMouse) return true; - if (mKey != rhs.mKey) return true; - if (mMask != rhs.mMask) return true; - if (mIgnoreMasks != rhs.mIgnoreMasks) return true; - return false; + return ! (*this == rhs); } bool LLKeyData::canHandle(const LLKeyData& data) const diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h index 1bbb2fadb5..68efe38689 100644 --- a/indra/llcommon/llkeybind.h +++ b/indra/llcommon/llkeybind.h @@ -44,8 +44,8 @@ public: bool empty() const { return isEmpty(); }; void reset(); LLKeyData& operator=(const LLKeyData& rhs); - bool operator==(const LLKeyData& rhs); - bool operator!=(const LLKeyData& rhs); + bool operator==(const LLKeyData& rhs) const; + bool operator!=(const LLKeyData& rhs) const; bool canHandle(const LLKeyData& data) const; bool canHandle(EMouseClickType mouse, KEY key, MASK mask) const; -- cgit v1.2.3 From d7f6b96e36c3cadb7a2731c6417709ee9668f2d7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 26 Sep 2024 12:35:27 -0400 Subject: Reinstate our lerp() function, avoid "math.h" header. For reasons that remain unclear, MSVC likes our lerp() function better than its own std::lerp() function: publishing the latter into the global namespace, instead of defining our own, produces fatal argument conversion warnings. "math.h" publishes all of into the global namespace, which causes a GCC conflict between std::lerp() and our lerp() function. Including instead leaves std::lerp() in the std namespace, eliminating the conflict. --- indra/llmath/llinterp.h | 4 ++-- indra/llmath/llmath.h | 11 ++++++++--- indra/newview/llcylinder.cpp | 2 +- indra/newview/llsky.cpp | 2 +- indra/newview/llsprite.cpp | 2 +- indra/newview/llworldmipmap.cpp | 2 +- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/indra/llmath/llinterp.h b/indra/llmath/llinterp.h index f4faa82a82..386679ffe9 100644 --- a/indra/llmath/llinterp.h +++ b/indra/llmath/llinterp.h @@ -29,11 +29,11 @@ #if defined(LL_WINDOWS) // macro definitions for common math constants (e.g. M_PI) are declared under the _USE_MATH_DEFINES // on Windows system. -// So, let's define _USE_MATH_DEFINES before including math.h +// So, let's define _USE_MATH_DEFINES before including cmath #define _USE_MATH_DEFINES #endif -#include "math.h" +#include // Class from which different types of interpolators can be derived diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index 09b685a68b..75284ef57e 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -358,9 +358,14 @@ inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs) return new_foo; } -// We used to define a simple lerp(F32, F32, F32) that probably predated -// std::lerp(). By now, do we need our own definition any longer? -using std::lerp; +// Even though there's now a std::lerp() function that appears to do the same +// as this function, for some reason MSVC likes this one better. Publishing +// std::lerp() into the global namespace instead of defining this function +// results in fatal argument conversion warnings. +inline F32 lerp(F32 a, F32 b, F32 u) +{ + return a + ((b - a) * u); +} inline F32 lerp2d(F32 x00, F32 x01, F32 x10, F32 x11, F32 u, F32 v) { diff --git a/indra/newview/llcylinder.cpp b/indra/newview/llcylinder.cpp index c347d3e1be..d5f1e25151 100644 --- a/indra/newview/llcylinder.cpp +++ b/indra/newview/llcylinder.cpp @@ -29,7 +29,7 @@ #include "llcylinder.h" #include "llerror.h" -#include "math.h" +#include #include "llmath.h" #include "noise.h" #include "v3math.h" diff --git a/indra/newview/llsky.cpp b/indra/newview/llsky.cpp index 82caa14433..66fbe0029a 100644 --- a/indra/newview/llsky.cpp +++ b/indra/newview/llsky.cpp @@ -39,7 +39,7 @@ // linden library includes #include "llerror.h" #include "llmath.h" -#include "math.h" +#include #include "v4color.h" #include "llviewerobjectlist.h" diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp index e51aeb6080..09c57eaf1d 100644 --- a/indra/newview/llsprite.cpp +++ b/indra/newview/llsprite.cpp @@ -37,7 +37,7 @@ #include "llsprite.h" -#include "math.h" +#include #include "lldrawable.h" #include "llface.h" diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp index d8ea2b884f..d198f7a33b 100644 --- a/indra/newview/llworldmipmap.cpp +++ b/indra/newview/llworldmipmap.cpp @@ -30,7 +30,7 @@ #include "llviewercontrol.h" // LLControlGroup #include "llviewertexturelist.h" -#include "math.h" // log() +#include // log() // Turn this on to output tile stats in the standard output #define DEBUG_TILES_STAT 0 -- cgit v1.2.3 From 15dad1387f43cb93f9822329c5e4c2061adb6ec2 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Thu, 26 Sep 2024 20:36:08 +0300 Subject: Set continue-on-error for linux builds --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index abab662db7..723295bfd7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -50,6 +50,7 @@ jobs: configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} include: - runner: linux-large + continue-on-error: true configuration: ReleaseOS runs-on: ${{ matrix.runner }} outputs: -- cgit v1.2.3 From 452e07489b95e8ba60bf585878f9c0ed8e13b5e3 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Thu, 26 Sep 2024 20:38:37 +0300 Subject: Remove whitespace for pre-commit --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 723295bfd7..9dbdf00f82 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -50,7 +50,7 @@ jobs: configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} include: - runner: linux-large - continue-on-error: true + continue-on-error: true configuration: ReleaseOS runs-on: ${{ matrix.runner }} outputs: -- cgit v1.2.3 From 8a6da4d6210192ada4a26ddd397c00d78b08dfcc Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 26 Sep 2024 13:48:58 -0400 Subject: Rename our lerp() to flerp(); call where MSVC balks at std::lerp(). Now the lerp() in global namespace is std::lerp(), but it remains true that for some calls to std::lerp(), MSVC issues fatal argument conversion warnings. In those places, call flerp() (our historic lerp()) instead. --- indra/llmath/llmath.h | 8 ++++---- indra/llmath/llvolume.cpp | 28 ++++++++++++++-------------- indra/newview/llpanelprimmediacontrols.cpp | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index 75284ef57e..1d6d986207 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -358,11 +358,11 @@ inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs) return new_foo; } +using std::lerp; // Even though there's now a std::lerp() function that appears to do the same -// as this function, for some reason MSVC likes this one better. Publishing -// std::lerp() into the global namespace instead of defining this function -// results in fatal argument conversion warnings. -inline F32 lerp(F32 a, F32 b, F32 u) +// as this function, in some cases MSVC likes this one better: some calls to +// std::lerp() produce fatal argument conversion warnings. +inline F32 flerp(F32 a, F32 b, F32 u) { return a + ((b - a) * u); } diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 700e61467b..275e5bcb43 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1294,9 +1294,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) - + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), + pt->mPos.set(0 + flerp(0,params.getShear().mV[0],s) + + flerp(-skew ,skew, t) * 0.5f, + c + flerp(0,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), hole_y * lerp(taper_y_begin, taper_y_end, t), @@ -1327,9 +1327,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); s = sin(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) - + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), + pt->mPos.set(0 + flerp(0,params.getShear().mV[0],s) + + flerp(-skew ,skew, t) * 0.5f, + c + flerp(0,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), @@ -1354,9 +1354,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); s = sin(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) - + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), + pt->mPos.set(0 + flerp(0,params.getShear().mV[0],s) + + flerp(-skew ,skew, t) * 0.5f, + c + flerp(0,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), hole_y * lerp(taper_y_begin, taper_y_end, t), @@ -1494,8 +1494,8 @@ bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split, for (S32 i=0;i= mControlFadeTime) { -- cgit v1.2.3 From fcfcca977c23e318769171f06fc8554869495870 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 26 Sep 2024 16:44:56 -0400 Subject: Ditch last instances of LL_LIBRARY_INCLUDE. --- indra/llcommon/llerror.h | 5 ----- indra/llcommon/llpointer.h | 5 ----- 2 files changed, 10 deletions(-) diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index b17b9ff21e..b6d560a121 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -239,17 +239,12 @@ namespace LLError ~CallSite(); -#ifdef LL_LIBRARY_INCLUDE - bool shouldLog(); -#else // LL_LIBRARY_INCLUDE bool shouldLog() { return mCached ? mShouldLog : Log::shouldLog(*this); } - // this member function needs to be in-line for efficiency -#endif // LL_LIBRARY_INCLUDE void invalidate(); diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index 71c955c4c5..1b2a961c40 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -189,10 +189,6 @@ public: } protected: -#ifdef LL_LIBRARY_INCLUDE - void ref(); - void unref(); -#else void ref() { if (mPointer) @@ -215,7 +211,6 @@ protected: } } } -#endif // LL_LIBRARY_INCLUDE protected: Type* mPointer; -- cgit v1.2.3 From d3833b6d618ff05f44c28eab67bc91ee5a3ba92d Mon Sep 17 00:00:00 2001 From: Ansariel Hiller Date: Fri, 27 Sep 2024 03:35:42 +0200 Subject: Fix lerp issues and eliminate flerp in favor of std::lerp (#2712) --- indra/llmath/llmath.h | 9 +-------- indra/llmath/llvolume.cpp | 28 ++++++++++++++-------------- indra/newview/llpanelprimmediacontrols.cpp | 2 +- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index 1d6d986207..0348dad8fe 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -359,13 +359,6 @@ inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs) } using std::lerp; -// Even though there's now a std::lerp() function that appears to do the same -// as this function, in some cases MSVC likes this one better: some calls to -// std::lerp() produce fatal argument conversion warnings. -inline F32 flerp(F32 a, F32 b, F32 u) -{ - return a + ((b - a) * u); -} inline F32 lerp2d(F32 x00, F32 x01, F32 x10, F32 x11, F32 u, F32 v) { @@ -490,7 +483,7 @@ inline U32 get_next_power_two(U32 val, U32 max_power_two) //get the gaussian value given the linear distance from axis x and guassian value o inline F32 llgaussian(F32 x, F32 o) { - return 1.f/(F_SQRT_TWO_PI*o)*powf(F_E, -(x*x)/(2*o*o)); + return 1.f/(F_SQRT_TWO_PI*o)*powf(F_E, -(x*x)/(2.f*o*o)); } //helper function for removing outliers diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 275e5bcb43..00a56edf68 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1294,9 +1294,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + flerp(0,params.getShear().mV[0],s) - + flerp(-skew ,skew, t) * 0.5f, - c + flerp(0,params.getShear().mV[1],s), + pt->mPos.set(0 + lerp(0.f,params.getShear().mV[0],s) + + lerp(-skew ,skew, t) * 0.5f, + c + lerp(0.f,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), hole_y * lerp(taper_y_begin, taper_y_end, t), @@ -1327,9 +1327,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); s = sin(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + flerp(0,params.getShear().mV[0],s) - + flerp(-skew ,skew, t) * 0.5f, - c + flerp(0,params.getShear().mV[1],s), + pt->mPos.set(0 + lerp(0.f,params.getShear().mV[0],s) + + lerp(-skew ,skew, t) * 0.5f, + c + lerp(0.f,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), @@ -1354,9 +1354,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); s = sin(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + flerp(0,params.getShear().mV[0],s) - + flerp(-skew ,skew, t) * 0.5f, - c + flerp(0,params.getShear().mV[1],s), + pt->mPos.set(0 + lerp(0.f,params.getShear().mV[0],s) + + lerp(-skew ,skew, t) * 0.5f, + c + lerp(0.f,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), hole_y * lerp(taper_y_begin, taper_y_end, t), @@ -1494,8 +1494,8 @@ bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split, for (S32 i=0;i= mControlFadeTime) { -- cgit v1.2.3 From a2b76b69ca1a29e40616d9279a25f9dd22a81127 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 27 Sep 2024 05:38:39 -0400 Subject: Migrate ~LLPointer()'s peculiar warning case to llpointer.cpp. This allows removing #include "llerror.h" from llpointer.h. Also remove #include "llmutex.h" as a heavy way to get . That requires adding #include "llmutex.h" to llimage.h, llnotifications.h, llwatchdog.cpp and llvolumemgr.cpp, which were inheriting it from llpointer.h. --- indra/llcommon/CMakeLists.txt | 1 + indra/llcommon/llpointer.cpp | 26 ++++++++++++++++++++++++++ indra/llcommon/llpointer.h | 19 ++++++++++++++----- indra/llimage/llimage.h | 5 +++-- indra/llmath/llvolumemgr.cpp | 1 + indra/llui/llnotifications.h | 1 + indra/newview/llwatchdog.cpp | 1 + 7 files changed, 47 insertions(+), 7 deletions(-) create mode 100755 indra/llcommon/llpointer.cpp diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 78bfaade55..aa8810f00b 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -70,6 +70,7 @@ set(llcommon_SOURCE_FILES llmetrics.cpp llmortician.cpp llmutex.cpp + llpointer.cpp llpredicate.cpp llprocess.cpp llprocessor.cpp diff --git a/indra/llcommon/llpointer.cpp b/indra/llcommon/llpointer.cpp new file mode 100755 index 0000000000..adea447caa --- /dev/null +++ b/indra/llcommon/llpointer.cpp @@ -0,0 +1,26 @@ +/** + * @file llpointer.cpp + * @author Nat Goodspeed + * @date 2024-09-26 + * @brief Implementation for llpointer. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llpointer.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llerror.h" + +void LLPointerBase::wild_dtor(std::string_view msg) +{ +// LL_WARNS() << msg << LL_ENDL; + llassert_msg(false, msg); +} diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index 1b2a961c40..b53cfcdd1a 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -26,8 +26,8 @@ #ifndef LLPOINTER_H #define LLPOINTER_H -#include "llerror.h" // *TODO: consider eliminating this -#include "llmutex.h" +#include +#include #include // std::swap() //---------------------------------------------------------------------------- @@ -43,8 +43,18 @@ //---------------------------------------------------------------------------- +class LLPointerBase +{ +protected: + // alert the coder that a referenced type's destructor did something very + // strange -- this is in a non-template base class so we can hide the + // implementation in llpointer.cpp + static void wild_dtor(std::string_view msg); +}; + // Note: relies on Type having ref() and unref() methods -template class LLPointer +template +class LLPointer: public LLPointerBase { public: template @@ -106,7 +116,6 @@ public: const Type& operator*() const { return *mPointer; } Type& operator*() { return *mPointer; } - operator BOOL() const { return (mPointer != nullptr); } operator bool() const { return (mPointer != nullptr); } bool operator!() const { return (mPointer == nullptr); } bool isNull() const { return (mPointer == nullptr); } @@ -206,7 +215,7 @@ protected: temp->unref(); if (mPointer != nullptr) { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; + wild_dtor("Unreference did assignment to non-NULL because of destructor"); unref(); } } diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 6b14b68c78..1fb61673bd 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -27,10 +27,11 @@ #ifndef LL_LLIMAGE_H #define LL_LLIMAGE_H -#include "lluuid.h" -#include "llstring.h" +#include "llmutex.h" #include "llpointer.h" +#include "llstring.h" #include "lltrace.h" +#include "lluuid.h" constexpr S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2 constexpr S32 MAX_IMAGE_MIP = 12; // 4096x4096 diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp index bb0c94d513..d8f649140f 100644 --- a/indra/llmath/llvolumemgr.cpp +++ b/indra/llmath/llvolumemgr.cpp @@ -25,6 +25,7 @@ #include "linden_common.h" +#include "llmutex.h" #include "llvolumemgr.h" #include "llvolume.h" diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 9b83da13ad..9d59b99349 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -94,6 +94,7 @@ #include "llinitparam.h" #include "llinstancetracker.h" #include "llmortician.h" +#include "llmutex.h" #include "llnotificationptr.h" #include "llpointer.h" #include "llrefcount.h" diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp index bf171fe954..d9fcd5811d 100644 --- a/indra/newview/llwatchdog.cpp +++ b/indra/newview/llwatchdog.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llwatchdog.h" +#include "llmutex.h" #include "llthread.h" constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U; -- cgit v1.2.3 From d39c862f856f45f83cd11ac5eb127a0b826a5b3f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 27 Sep 2024 05:40:54 -0400 Subject: Automate memory management in JPEG2KEncode and JPEG2KDecode. Instead of using strdup() and free() to store comment_text in JPEG2KEncode, store a std::string. Similarly, instead of manually managing pointers to encoder, decoder, image, stream and codestream_info in JPEG2KDecode and JPEG2KEncode, use std::unique_ptr for each, specifying their respective deleter functions. --- indra/llimagej2coj/llimagej2coj.cpp | 232 ++++++++++++++---------------------- 1 file changed, 88 insertions(+), 144 deletions(-) diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 6037517103..a2a43e4baa 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -31,6 +31,8 @@ #include "openjpeg.h" #include "event.h" #include "cio.h" +#include // std::unique_ptr +#include #define MAX_ENCODED_DISCARD_LEVELS 5 @@ -231,33 +233,6 @@ public: parameters.cp_reduce = discardLevel; } - ~JPEG2KDecode() - { - if (decoder) - { - opj_destroy_codec(decoder); - } - decoder = nullptr; - - if (image) - { - opj_image_destroy(image); - } - image = nullptr; - - if (stream) - { - opj_stream_destroy(stream); - } - stream = nullptr; - - if (codestream_info) - { - opj_destroy_cstr_info(&codestream_info); - } - codestream_info = nullptr; - } - bool readHeader( U8* data, U32 dataSize, @@ -268,46 +243,42 @@ public: { parameters.flags |= OPJ_DPARAMETERS_DUMP_FLAG; - decoder = opj_create_decompress(OPJ_CODEC_J2K); + decoder.reset(opj_create_decompress(OPJ_CODEC_J2K)); - if (!opj_setup_decoder(decoder, ¶meters)) + if (!opj_setup_decoder(decoder.get(), ¶meters)) { return false; } - if (stream) - { - opj_stream_destroy(stream); - } - - stream = opj_stream_create(dataSize, true); + stream.reset(opj_stream_create(dataSize, true)); if (!stream) { return false; } - opj_stream_set_user_data(stream, this, opj_free_user_data); - opj_stream_set_user_data_length(stream, dataSize); - opj_stream_set_read_function(stream, opj_read); - opj_stream_set_write_function(stream, opj_write); - opj_stream_set_skip_function(stream, opj_skip); - opj_stream_set_seek_function(stream, opj_seek); + opj_stream_set_user_data(stream.get(), this, opj_free_user_data); + opj_stream_set_user_data_length(stream.get(), dataSize); + opj_stream_set_read_function(stream.get(), opj_read); + opj_stream_set_write_function(stream.get(), opj_write); + opj_stream_set_skip_function(stream.get(), opj_skip); + opj_stream_set_seek_function(stream.get(), opj_seek); buffer = data; size = dataSize; offset = 0; // enable decoding partially loaded images - opj_decoder_set_strict_mode(decoder, OPJ_FALSE); + opj_decoder_set_strict_mode(decoder.get(), OPJ_FALSE); /* Read the main header of the codestream and if necessary the JP2 boxes*/ - if (!opj_read_header((opj_stream_t*)stream, decoder, &image)) + opj_image_t* img; + if (!opj_read_header(stream.get(), decoder.get(), &img)) { return false; } + image.reset(img); - codestream_info = opj_get_cstr_info(decoder); - + codestream_info.reset(opj_get_cstr_info(decoder.get())); if (!codestream_info) { return false; @@ -337,51 +308,44 @@ public: { parameters.flags &= ~OPJ_DPARAMETERS_DUMP_FLAG; - decoder = opj_create_decompress(OPJ_CODEC_J2K); - opj_setup_decoder(decoder, ¶meters); + decoder.reset(opj_create_decompress(OPJ_CODEC_J2K)); + opj_setup_decoder(decoder.get(), ¶meters); - opj_set_info_handler(decoder, opj_info, this); - opj_set_warning_handler(decoder, opj_warn, this); - opj_set_error_handler(decoder, opj_error, this); + opj_set_info_handler(decoder.get(), opj_info, this); + opj_set_warning_handler(decoder.get(), opj_warn, this); + opj_set_error_handler(decoder.get(), opj_error, this); - if (stream) - { - opj_stream_destroy(stream); - } - - stream = opj_stream_create(dataSize, true); + stream.reset(opj_stream_create(dataSize, true)); if (!stream) { return false; } - opj_stream_set_user_data(stream, this, opj_free_user_data); - opj_stream_set_user_data_length(stream, dataSize); - opj_stream_set_read_function(stream, opj_read); - opj_stream_set_write_function(stream, opj_write); - opj_stream_set_skip_function(stream, opj_skip); - opj_stream_set_seek_function(stream, opj_seek); + opj_stream_set_user_data(stream.get(), this, opj_free_user_data); + opj_stream_set_user_data_length(stream.get(), dataSize); + opj_stream_set_read_function(stream.get(), opj_read); + opj_stream_set_write_function(stream.get(), opj_write); + opj_stream_set_skip_function(stream.get(), opj_skip); + opj_stream_set_seek_function(stream.get(), opj_seek); buffer = data; size = dataSize; offset = 0; - if (image) - { - opj_image_destroy(image); - image = nullptr; - } + image.reset(); // needs to happen before opj_read_header and opj_decode... - opj_set_decoded_resolution_factor(decoder, discard_level); + opj_set_decoded_resolution_factor(decoder.get(), discard_level); // enable decoding partially loaded images - opj_decoder_set_strict_mode(decoder, OPJ_FALSE); + opj_decoder_set_strict_mode(decoder.get(), OPJ_FALSE); - if (!opj_read_header(stream, decoder, &image)) + opj_image_t* img; + if (!opj_read_header(stream.get(), decoder.get(), &img)) { return false; } + image.reset(img); // needs to happen before decode which may fail if (channels) @@ -389,30 +353,36 @@ public: *channels = image->numcomps; } - OPJ_BOOL decoded = opj_decode(decoder, stream, image); + OPJ_BOOL decoded = opj_decode(decoder.get(), stream.get(), image.get()); // count was zero. The latter is just a sanity check before we // dereference the array. - if (!decoded || !image || !image->numcomps) - { - opj_end_decompress(decoder, stream); - return false; - } - - opj_end_decompress(decoder, stream); - - return true; + bool result = (decoded && image && image->numcomps); + opj_end_decompress(decoder.get(), stream.get()); + return result; } - opj_image_t* getImage() { return image; } + opj_image_t* getImage() { return image.get(); } private: + // opj_destroy_cstr_info(opj_codestream_info_v2_t**) requires a pointer to + // pointer, which is too bad because otherwise we could directly pass that + // function as the unique_ptr's deleter. + static void cstr_info_deleter(opj_codestream_info_v2_t* doomed) + { + opj_destroy_cstr_info(&doomed); + } + opj_dparameters_t parameters; opj_event_mgr_t event_mgr; - opj_image_t* image = nullptr; - opj_codec_t* decoder = nullptr; - opj_stream_t* stream = nullptr; - opj_codestream_info_v2_t* codestream_info = nullptr; + std::unique_ptr + codestream_info{ nullptr, cstr_info_deleter }; + std::unique_ptr + stream{ nullptr, opj_stream_destroy }; + std::unique_ptr + image{ nullptr, opj_image_destroy }; + std::unique_ptr + decoder{ nullptr, opj_destroy_codec }; }; class JPEG2KEncode : public JPEG2KBase @@ -447,43 +417,19 @@ public: parameters.irreversible = 1; } - if (comment_text) - { - free(comment_text); - } - comment_text = comment_text_in ? strdup(comment_text_in) : nullptr; + comment_text = { comment_text_in? comment_text_in : "no comment" }; - parameters.cp_comment = comment_text ? comment_text : (char*)"no comment"; + // Because comment_text is a member declared before parameters, + // it will outlive parameters, so we can safely store in parameters a + // pointer into comment_text's data. Unfortunately cp_comment is + // declared as (non-const) char*. We just have to trust that this is + // legacy C style coding, rather than any intention to modify the + // comment string. (If there was actual modification, we could use a + // std::vector instead, but let's only go there if we must.) + parameters.cp_comment = const_cast(comment_text.c_str()); llassert(parameters.cp_comment); } - ~JPEG2KEncode() - { - if (encoder) - { - opj_destroy_codec(encoder); - } - encoder = nullptr; - - if (image) - { - opj_image_destroy(image); - } - image = nullptr; - - if (stream) - { - opj_stream_destroy(stream); - } - stream = nullptr; - - if (comment_text) - { - free(comment_text); - } - comment_text = nullptr; - } - bool encode(const LLImageRaw& rawImageIn, LLImageJ2C &compressedImageOut) { LLImageDataSharedLock lockIn(&rawImageIn); @@ -491,7 +437,7 @@ public: setImage(rawImageIn); - encoder = opj_create_compress(OPJ_CODEC_J2K); + encoder.reset(opj_create_compress(OPJ_CODEC_J2K)); parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0; parameters.cod_format = OPJ_CODEC_J2K; @@ -542,14 +488,14 @@ public: parameters.max_cs_size = max_cs_size; } - if (!opj_setup_encoder(encoder, ¶meters, image)) + if (!opj_setup_encoder(encoder.get(), ¶meters, image.get())) { return false; } - opj_set_info_handler(encoder, opj_info, this); - opj_set_warning_handler(encoder, opj_warn, this); - opj_set_error_handler(encoder, opj_error, this); + opj_set_info_handler(encoder.get(), opj_info, this); + opj_set_warning_handler(encoder.get(), opj_warn, this); + opj_set_error_handler(encoder.get(), opj_error, this); U32 tile_count = (rawImageIn.getWidth() >> 6) * (rawImageIn.getHeight() >> 6); U32 data_size_guess = tile_count * TILE_SIZE; @@ -561,37 +507,32 @@ public: memset(buffer, 0, data_size_guess); - if (stream) - { - opj_stream_destroy(stream); - } - - stream = opj_stream_create(data_size_guess, false); + stream.reset(opj_stream_create(data_size_guess, false)); if (!stream) { return false; } - opj_stream_set_user_data(stream, this, opj_free_user_data_write); - opj_stream_set_user_data_length(stream, data_size_guess); - opj_stream_set_read_function(stream, opj_read); - opj_stream_set_write_function(stream, opj_write); - opj_stream_set_skip_function(stream, opj_skip); - opj_stream_set_seek_function(stream, opj_seek); + opj_stream_set_user_data(stream.get(), this, opj_free_user_data_write); + opj_stream_set_user_data_length(stream.get(), data_size_guess); + opj_stream_set_read_function(stream.get(), opj_read); + opj_stream_set_write_function(stream.get(), opj_write); + opj_stream_set_skip_function(stream.get(), opj_skip); + opj_stream_set_seek_function(stream.get(), opj_seek); - OPJ_BOOL started = opj_start_compress(encoder, image, stream); + OPJ_BOOL started = opj_start_compress(encoder.get(), image.get(), stream.get()); if (!started) { return false; } - if (!opj_encode(encoder, stream)) + if (!opj_encode(encoder.get(), stream.get())) { return false; } - OPJ_BOOL encoded = opj_end_compress(encoder, stream); + OPJ_BOOL encoded = opj_end_compress(encoder.get(), stream.get()); // if we successfully encoded, then stream out the compressed data... if (encoded) @@ -624,7 +565,7 @@ public: cmptparm[c].h = height; } - image = opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB); + image.reset(opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB)); image->x1 = width; image->y1 = height; @@ -739,15 +680,18 @@ public: }*/ } - opj_image_t* getImage() { return image; } + opj_image_t* getImage() { return image.get(); } private: + std::string comment_text; opj_cparameters_t parameters; opj_event_mgr_t event_mgr; - opj_image_t* image = nullptr; - opj_codec_t* encoder = nullptr; - opj_stream_t* stream = nullptr; - char* comment_text = nullptr; + std::unique_ptr + stream{ nullptr, opj_stream_destroy }; + std::unique_ptr + image{ nullptr, opj_image_destroy }; + std::unique_ptr + encoder{ nullptr, opj_destroy_codec }; }; -- cgit v1.2.3 From c7cef952dadf382bd9460ded1b4d1ac6273eff7a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 27 Sep 2024 06:05:32 -0400 Subject: Allow Windows and Mac builds to succeed despite Linux failure. --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9dbdf00f82..cd54e90e02 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -53,6 +53,7 @@ jobs: continue-on-error: true configuration: ReleaseOS runs-on: ${{ matrix.runner }} + continue-on-error: ${{ matrix.continue-on-error }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} viewer_version: ${{ steps.build.outputs.viewer_version }} -- cgit v1.2.3 From 51fb298f08b281962254ec08fc3ae82c92082d0e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 27 Sep 2024 06:12:49 -0400 Subject: Try again to let Windows and Mac succeed despite Linux failure. --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index cd54e90e02..38e241283d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -53,7 +53,6 @@ jobs: continue-on-error: true configuration: ReleaseOS runs-on: ${{ matrix.runner }} - continue-on-error: ${{ matrix.continue-on-error }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} viewer_version: ${{ steps.build.outputs.viewer_version }} @@ -156,6 +155,7 @@ jobs: env: AUTOBUILD_VCS_BRANCH: ${{ steps.which-branch.outputs.branch }} RUNNER_OS: ${{ runner.os }} + continue-on-error: ${{ matrix.continue-on-error }} run: | # set up things the viewer's build.sh script expects set -x -- cgit v1.2.3 From ecc2ada086b7cdc943abe11eb62b749c2b3d8798 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 27 Sep 2024 06:22:56 -0400 Subject: lltexlayerparams.h uses std::atomic, so #include --- indra/llappearance/lltexlayerparams.h | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llappearance/lltexlayerparams.h b/indra/llappearance/lltexlayerparams.h index 5e785e4f3e..116eb3bee0 100644 --- a/indra/llappearance/lltexlayerparams.h +++ b/indra/llappearance/lltexlayerparams.h @@ -30,6 +30,7 @@ #include "llpointer.h" #include "v4color.h" #include "llviewervisualparam.h" +#include class LLAvatarAppearance; class LLImageRaw; -- cgit v1.2.3 From 5c9d60c0e58e2dc65e3e048bd42412997770c6d6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 27 Sep 2024 06:29:49 -0400 Subject: Linux GCC prefers the explicit std::string::assign() call. --- indra/llimagej2coj/llimagej2coj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index a2a43e4baa..6af321be3d 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -417,7 +417,7 @@ public: parameters.irreversible = 1; } - comment_text = { comment_text_in? comment_text_in : "no comment" }; + comment_text.assign(comment_text_in? comment_text_in : "no comment"); // Because comment_text is a member declared before parameters, // it will outlive parameters, so we can safely store in parameters a -- cgit v1.2.3 From 1c78829ab3177940adbdb7f0081b5f46c1e37763 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 27 Sep 2024 11:07:08 -0400 Subject: Introduce owning_ptr; use it for JPEG2KEncode and JPEG2KDecode. owning_ptr adapts std::unique_ptr to be a better drop-in replacement for a legacy class that formerly stored plain T* data members, and explicitly destroyed them using domain-specific destructor functions. Directly substituting std::unique_ptr into JPEG2KEncode and JPEG2KDecode was cumbersome because every such pointer declaration required a redundant template parameter describing the deleter function passed into its constructor. Moreover, it required lots of little syntax tweaks: changing every assignment to a reset() call, changing every reference to a get() call. Using owning_ptr allows us to leave the code more or less as it was before, save that assignment and destruction automatically handle the previous referenced T instance. --- indra/llcommon/owning_ptr.h | 71 ++++++++++++++++++ indra/llimagej2coj/llimagej2coj.cpp | 145 +++++++++++++++++------------------- 2 files changed, 140 insertions(+), 76 deletions(-) create mode 100755 indra/llcommon/owning_ptr.h diff --git a/indra/llcommon/owning_ptr.h b/indra/llcommon/owning_ptr.h new file mode 100755 index 0000000000..c0da245265 --- /dev/null +++ b/indra/llcommon/owning_ptr.h @@ -0,0 +1,71 @@ +/** + * @file owning_ptr.h + * @author Nat Goodspeed + * @date 2024-09-27 + * @brief owning_ptr is like std::unique_ptr, but easier to integrate + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_OWNING_PTR_H) +#define LL_OWNING_PTR_H + +#include +#include + +/** + * owning_ptr adapts std::unique_ptr to make it easier to adopt into + * older code using dumb pointers. + * + * Consider a class Outer with a member Thing* mThing. After the constructor, + * each time a method wants to assign to mThing, it must test for nullptr and + * destroy the previous Thing instance. During Outer's lifetime, mThing is + * passed to legacy domain-specific functions accepting plain Thing*. Finally + * the destructor must again test for nullptr and destroy the remaining Thing + * instance. + * + * Multiply that by several different Outer members of different types, + * possibly with different domain-specific destructor functions. + * + * Dropping std::unique_ptr into Outer is cumbersome for a several + * reasons. First, if Thing requires a domain-specific destructor function, + * the unique_ptr declaration of mThing must explicitly state the type of that + * function (as a function pointer, for a typical legacy function). Second, + * every Thing* assignment to mThing must be changed to mThing.reset(). Third, + * every time we call a legacy domain-specific function, we must pass + * mThing.get(). + * + * owning_ptr is designed to drop into a situation like this. The domain- + * specific destructor function, if any, is passed to its constructor; it need + * not be encoded into the pointer type. owning_ptr supports plain pointer + * assignment, internally calling std::unique_ptr::reset(). It also + * supports implicit conversion to plain T*, to pass the owned pointer to + * legacy domain-specific functions. + * + * Obviously owning_ptr must not be used in situations where ownership of + * the referenced object is passed on to another pointer: use std::unique_ptr + * for that. Similarly, it is not for shared ownership. It simplifies lifetime + * management for classes that currently store (and explicitly destroy) plain + * T* pointers. + */ +template +class owning_ptr +{ + using deleter = std::function; +public: + owning_ptr(T* p=nullptr, const deleter& d=std::default_delete()): + mPtr(p, d) + {} + void reset(T* p=nullptr) { mPtr.reset(p); } + owning_ptr& operator=(T* p) { mPtr.reset(p); return *this; } + operator T*() const { return mPtr.get(); } + T& operator*() const { return *mPtr; } + T* operator->() const { return mPtr.operator->(); } + +private: + std::unique_ptr> mPtr; +}; + +#endif /* ! defined(LL_OWNING_PTR_H) */ diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 6af321be3d..d8f17561b3 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -31,7 +31,7 @@ #include "openjpeg.h" #include "event.h" #include "cio.h" -#include // std::unique_ptr +#include "owning_ptr.h" #include #define MAX_ENCODED_DISCARD_LEVELS 5 @@ -243,42 +243,42 @@ public: { parameters.flags |= OPJ_DPARAMETERS_DUMP_FLAG; - decoder.reset(opj_create_decompress(OPJ_CODEC_J2K)); + decoder = opj_create_decompress(OPJ_CODEC_J2K); - if (!opj_setup_decoder(decoder.get(), ¶meters)) + if (!opj_setup_decoder(decoder, ¶meters)) { return false; } - stream.reset(opj_stream_create(dataSize, true)); + stream = opj_stream_create(dataSize, true); if (!stream) { return false; } - opj_stream_set_user_data(stream.get(), this, opj_free_user_data); - opj_stream_set_user_data_length(stream.get(), dataSize); - opj_stream_set_read_function(stream.get(), opj_read); - opj_stream_set_write_function(stream.get(), opj_write); - opj_stream_set_skip_function(stream.get(), opj_skip); - opj_stream_set_seek_function(stream.get(), opj_seek); + opj_stream_set_user_data(stream, this, opj_free_user_data); + opj_stream_set_user_data_length(stream, dataSize); + opj_stream_set_read_function(stream, opj_read); + opj_stream_set_write_function(stream, opj_write); + opj_stream_set_skip_function(stream, opj_skip); + opj_stream_set_seek_function(stream, opj_seek); buffer = data; size = dataSize; offset = 0; // enable decoding partially loaded images - opj_decoder_set_strict_mode(decoder.get(), OPJ_FALSE); + opj_decoder_set_strict_mode(decoder, OPJ_FALSE); /* Read the main header of the codestream and if necessary the JP2 boxes*/ opj_image_t* img; - if (!opj_read_header(stream.get(), decoder.get(), &img)) + if (!opj_read_header(stream, decoder, &img)) { return false; } - image.reset(img); + image = img; - codestream_info.reset(opj_get_cstr_info(decoder.get())); + codestream_info = opj_get_cstr_info(decoder); if (!codestream_info) { return false; @@ -308,44 +308,44 @@ public: { parameters.flags &= ~OPJ_DPARAMETERS_DUMP_FLAG; - decoder.reset(opj_create_decompress(OPJ_CODEC_J2K)); - opj_setup_decoder(decoder.get(), ¶meters); + decoder = opj_create_decompress(OPJ_CODEC_J2K); + opj_setup_decoder(decoder, ¶meters); - opj_set_info_handler(decoder.get(), opj_info, this); - opj_set_warning_handler(decoder.get(), opj_warn, this); - opj_set_error_handler(decoder.get(), opj_error, this); + opj_set_info_handler(decoder, opj_info, this); + opj_set_warning_handler(decoder, opj_warn, this); + opj_set_error_handler(decoder, opj_error, this); - stream.reset(opj_stream_create(dataSize, true)); + stream = opj_stream_create(dataSize, true); if (!stream) { return false; } - opj_stream_set_user_data(stream.get(), this, opj_free_user_data); - opj_stream_set_user_data_length(stream.get(), dataSize); - opj_stream_set_read_function(stream.get(), opj_read); - opj_stream_set_write_function(stream.get(), opj_write); - opj_stream_set_skip_function(stream.get(), opj_skip); - opj_stream_set_seek_function(stream.get(), opj_seek); + opj_stream_set_user_data(stream, this, opj_free_user_data); + opj_stream_set_user_data_length(stream, dataSize); + opj_stream_set_read_function(stream, opj_read); + opj_stream_set_write_function(stream, opj_write); + opj_stream_set_skip_function(stream, opj_skip); + opj_stream_set_seek_function(stream, opj_seek); buffer = data; size = dataSize; offset = 0; - image.reset(); + image = nullptr; // needs to happen before opj_read_header and opj_decode... - opj_set_decoded_resolution_factor(decoder.get(), discard_level); + opj_set_decoded_resolution_factor(decoder, discard_level); // enable decoding partially loaded images - opj_decoder_set_strict_mode(decoder.get(), OPJ_FALSE); + opj_decoder_set_strict_mode(decoder, OPJ_FALSE); opj_image_t* img; - if (!opj_read_header(stream.get(), decoder.get(), &img)) + if (!opj_read_header(stream, decoder, &img)) { return false; } - image.reset(img); + image = img; // needs to happen before decode which may fail if (channels) @@ -353,36 +353,32 @@ public: *channels = image->numcomps; } - OPJ_BOOL decoded = opj_decode(decoder.get(), stream.get(), image.get()); + OPJ_BOOL decoded = opj_decode(decoder, stream, image); // count was zero. The latter is just a sanity check before we // dereference the array. bool result = (decoded && image && image->numcomps); - opj_end_decompress(decoder.get(), stream.get()); + opj_end_decompress(decoder, stream); return result; } - opj_image_t* getImage() { return image.get(); } + opj_image_t* getImage() { return image; } private: - // opj_destroy_cstr_info(opj_codestream_info_v2_t**) requires a pointer to - // pointer, which is too bad because otherwise we could directly pass that - // function as the unique_ptr's deleter. - static void cstr_info_deleter(opj_codestream_info_v2_t* doomed) - { - opj_destroy_cstr_info(&doomed); - } - opj_dparameters_t parameters; opj_event_mgr_t event_mgr; - std::unique_ptr - codestream_info{ nullptr, cstr_info_deleter }; - std::unique_ptr - stream{ nullptr, opj_stream_destroy }; - std::unique_ptr - image{ nullptr, opj_image_destroy }; - std::unique_ptr - decoder{ nullptr, opj_destroy_codec }; + owning_ptr codestream_info{ + nullptr, + // opj_destroy_cstr_info(opj_codestream_info_v2_t**) requires a + // pointer to pointer, which is too bad because otherwise we could + // directly pass that function as the owning_ptr's deleter. + [](opj_codestream_info_v2_t* doomed) + { + opj_destroy_cstr_info(&doomed); + }}; + owning_ptr stream{ nullptr, opj_stream_destroy }; + owning_ptr image{ nullptr, opj_image_destroy }; + owning_ptr decoder{ nullptr, opj_destroy_codec }; }; class JPEG2KEncode : public JPEG2KBase @@ -437,7 +433,7 @@ public: setImage(rawImageIn); - encoder.reset(opj_create_compress(OPJ_CODEC_J2K)); + encoder = opj_create_compress(OPJ_CODEC_J2K); parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0; parameters.cod_format = OPJ_CODEC_J2K; @@ -488,14 +484,14 @@ public: parameters.max_cs_size = max_cs_size; } - if (!opj_setup_encoder(encoder.get(), ¶meters, image.get())) + if (!opj_setup_encoder(encoder, ¶meters, image)) { return false; } - opj_set_info_handler(encoder.get(), opj_info, this); - opj_set_warning_handler(encoder.get(), opj_warn, this); - opj_set_error_handler(encoder.get(), opj_error, this); + opj_set_info_handler(encoder, opj_info, this); + opj_set_warning_handler(encoder, opj_warn, this); + opj_set_error_handler(encoder, opj_error, this); U32 tile_count = (rawImageIn.getWidth() >> 6) * (rawImageIn.getHeight() >> 6); U32 data_size_guess = tile_count * TILE_SIZE; @@ -507,32 +503,32 @@ public: memset(buffer, 0, data_size_guess); - stream.reset(opj_stream_create(data_size_guess, false)); + stream = opj_stream_create(data_size_guess, false); if (!stream) { return false; } - opj_stream_set_user_data(stream.get(), this, opj_free_user_data_write); - opj_stream_set_user_data_length(stream.get(), data_size_guess); - opj_stream_set_read_function(stream.get(), opj_read); - opj_stream_set_write_function(stream.get(), opj_write); - opj_stream_set_skip_function(stream.get(), opj_skip); - opj_stream_set_seek_function(stream.get(), opj_seek); + opj_stream_set_user_data(stream, this, opj_free_user_data_write); + opj_stream_set_user_data_length(stream, data_size_guess); + opj_stream_set_read_function(stream, opj_read); + opj_stream_set_write_function(stream, opj_write); + opj_stream_set_skip_function(stream, opj_skip); + opj_stream_set_seek_function(stream, opj_seek); - OPJ_BOOL started = opj_start_compress(encoder.get(), image.get(), stream.get()); + OPJ_BOOL started = opj_start_compress(encoder, image, stream); if (!started) { return false; } - if (!opj_encode(encoder.get(), stream.get())) + if (!opj_encode(encoder, stream)) { return false; } - OPJ_BOOL encoded = opj_end_compress(encoder.get(), stream.get()); + OPJ_BOOL encoded = opj_end_compress(encoder, stream); // if we successfully encoded, then stream out the compressed data... if (encoded) @@ -565,7 +561,7 @@ public: cmptparm[c].h = height; } - image.reset(opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB)); + image = opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB); image->x1 = width; image->y1 = height; @@ -680,18 +676,15 @@ public: }*/ } - opj_image_t* getImage() { return image.get(); } + opj_image_t* getImage() { return image; } private: - std::string comment_text; - opj_cparameters_t parameters; - opj_event_mgr_t event_mgr; - std::unique_ptr - stream{ nullptr, opj_stream_destroy }; - std::unique_ptr - image{ nullptr, opj_image_destroy }; - std::unique_ptr - encoder{ nullptr, opj_destroy_codec }; + std::string comment_text; + opj_cparameters_t parameters; + opj_event_mgr_t event_mgr; + owning_ptr stream{ nullptr, opj_stream_destroy }; + owning_ptr image{ nullptr, opj_image_destroy }; + owning_ptr encoder{ nullptr, opj_destroy_codec }; }; -- cgit v1.2.3 From cf2f482ceca796e587dfe2fb8df552c09156fb50 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 27 Sep 2024 11:11:29 -0400 Subject: Slightly streamline owning_ptr class definition. --- indra/llcommon/owning_ptr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/owning_ptr.h b/indra/llcommon/owning_ptr.h index c0da245265..7cf8d3f0ba 100755 --- a/indra/llcommon/owning_ptr.h +++ b/indra/llcommon/owning_ptr.h @@ -65,7 +65,7 @@ public: T* operator->() const { return mPtr.operator->(); } private: - std::unique_ptr> mPtr; + std::unique_ptr mPtr; }; #endif /* ! defined(LL_OWNING_PTR_H) */ -- cgit v1.2.3 From 3904a15c63dbebca439ecf62f07234e1895dc93b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 2 Oct 2024 17:35:17 +0300 Subject: viewer#2705 Some sky parameters weren't updating --- indra/llinventory/llsettingssky.cpp | 8 ++++++++ indra/newview/llsettingsvo.cpp | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp index 3685915ffd..ce6244d038 100644 --- a/indra/llinventory/llsettingssky.cpp +++ b/indra/llinventory/llsettingssky.cpp @@ -1995,6 +1995,7 @@ F32 LLSettingsSky::getCloudShadow() const void LLSettingsSky::setCloudShadow(F32 val) { mCloudShadow = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2051,6 +2052,7 @@ F32 LLSettingsSky::getMaxY() const void LLSettingsSky::setMaxY(F32 val) { mMaxY = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2062,6 +2064,7 @@ LLQuaternion LLSettingsSky::getMoonRotation() const void LLSettingsSky::setMoonRotation(const LLQuaternion &val) { mMoonRotation = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2073,6 +2076,7 @@ F32 LLSettingsSky::getMoonScale() const void LLSettingsSky::setMoonScale(F32 val) { mMoonScale = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2095,6 +2099,7 @@ F32 LLSettingsSky::getMoonBrightness() const void LLSettingsSky::setMoonBrightness(F32 brightness_factor) { mMoonBrightness = brightness_factor; + setDirtyFlag(true); setLLSDDirty(); } @@ -2131,6 +2136,7 @@ LLColor3 LLSettingsSky::getSunlightColorClamped() const void LLSettingsSky::setSunlightColor(const LLColor3 &val) { mSunlightColor = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2142,6 +2148,7 @@ LLQuaternion LLSettingsSky::getSunRotation() const void LLSettingsSky::setSunRotation(const LLQuaternion &val) { mSunRotation = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2153,6 +2160,7 @@ F32 LLSettingsSky::getSunScale() const void LLSettingsSky::setSunScale(F32 val) { mSunScale = val; + setDirtyFlag(true); setLLSDDirty(); } diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 34b8535c84..fee0bbcda4 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -671,7 +671,8 @@ void LLSettingsVOSky::updateSettings() // After some A/B comparison of relesae vs EEP, tweak to allow strength to fall below 2 // at night, for better match. (mSceneLightStrength is a divisor, so lower value means brighter // local lights) - F32 sun_dynamic_range = llmax(gSavedSettings.getF32("RenderSunDynamicRange"), 0.0001f); + LLCachedControl sdr(gSavedSettings, "RenderSunDynamicRange", 1.f); + F32 sun_dynamic_range = llmax(sdr(), 0.0001f); mSceneLightStrength = 2.0f * (0.75f + sun_dynamic_range * dp); gSky.setSunAndMoonDirectionsCFR(sun_direction, moon_direction); -- cgit v1.2.3 From 9d3c8475c62f1664b450e0056d60ef95acc52d97 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 3 Oct 2024 00:06:32 +0300 Subject: viewer#2709 Fix loose triangle #2 --- indra/llrender/llrender2dutils.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llrender/llrender2dutils.cpp b/indra/llrender/llrender2dutils.cpp index c3d5da0914..5b08c29d64 100644 --- a/indra/llrender/llrender2dutils.cpp +++ b/indra/llrender/llrender2dutils.cpp @@ -1693,10 +1693,10 @@ void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV); gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop); - gGL.vertex3fv((center_draw_rect.mTop* height_vec).mV); + gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); - gGL.vertex3fv((center_draw_rect.mLeft* width_vec + center_draw_rect.mTop * height_vec).mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV); gGL.texCoord2f(clip_rect.mLeft, clip_rect.mTop); gGL.vertex3fv((height_vec).mV); -- cgit v1.2.3 From dce44872e441f2437777fde95a5a3451782f42a1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 4 Oct 2024 10:52:09 -0400 Subject: Restore macos-14-large runner for Xcode 16 compiler. Also restore notice messages about the selected viewer channel. --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f515046da8..bfad0e467b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -46,7 +46,7 @@ jobs: needs: setup strategy: matrix: - runner: [windows-large, macos-12-xl, linux-large] + runner: [windows-large, macos-14-xlarge, linux-large] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} include: - runner: linux-large @@ -250,7 +250,7 @@ jobs: export viewer_channel="Second Life Test" fi fi - echo "viewer_channel=$viewer_channel" + echo "::notice::$RUNNER_OS viewer_channel=$viewer_channel" echo "viewer_channel=$viewer_channel" >> "$GITHUB_OUTPUT" # On windows we need to point the build to the correct python # as neither CMake's FindPython nor our custom Python.cmake module -- cgit v1.2.3 From 5836d8835f88af8b82154a48bcf1281937ed1f04 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 4 Oct 2024 18:33:28 +0300 Subject: Lua api to get info about nearby avatars and objects --- indra/newview/llagentlistener.cpp | 117 ++++++++++++++++++++++ indra/newview/llagentlistener.h | 4 + indra/newview/scripts/lua/require/LLAgent.lua | 36 +++++++ indra/newview/scripts/lua/require/UI.lua | 6 +- indra/newview/scripts/lua/test_nearby_avatars.lua | 25 +++++ 5 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 indra/newview/scripts/lua/test_nearby_avatars.lua diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 14e443ec4e..f00173dc13 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -32,11 +32,14 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llavatarname.h" +#include "llavatarnamecache.h" #include "llvoavatar.h" #include "llcommandhandler.h" #include "llinventorymodel.h" #include "llslurl.h" #include "llurldispatcher.h" +#include "llviewercontrol.h" #include "llviewernetwork.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -47,6 +50,7 @@ #include "lltoolgrab.h" #include "llhudeffectlookat.h" #include "llagentcamera.h" +#include "resultset.h" #include static const F64 PLAY_ANIM_THROTTLE_PERIOD = 1.f; @@ -176,6 +180,25 @@ LLAgentListener::LLAgentListener(LLAgent &agent) "Return information about [\"item_id\"] animation", &LLAgentListener::getAnimationInfo, llsd::map("item_id", LLSD(), "reply", LLSD())); + + add("getID", + "Return your own avatar ID", + &LLAgentListener::getID, + llsd::map("reply", LLSD())); + + add("getNearbyAvatarsList", + "Return result set key [\"result\"] for nearby avatars in a range of [\"dist\"]\n" + "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" + "reply contains \"result\" table with \"id\", \"name\", \"global_pos\", \"region_pos\" fields", + &LLAgentListener::getNearbyAvatarsList, + llsd::map("reply", LLSD())); + + add("getNearbyObjectsList", + "Return result set key [\"result\"] for nearby objects in a range of [\"dist\"]\n" + "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" + "reply contains \"result\" table with \"id\", \"global_pos\", \"region_pos\" fields", + &LLAgentListener::getNearbyObjectsList, + llsd::map("reply", LLSD())); } void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -693,3 +716,97 @@ void LLAgentListener::getAnimationInfo(LLSD const &event_data) "priority", motion->getPriority()); } } + +void LLAgentListener::getID(LLSD const& event_data) +{ + Response response(llsd::map("id", gAgentID), event_data); +} + +struct AvResultSet : public LL::ResultSet +{ + AvResultSet() : LL::ResultSet("nearby_avatars") {} + std::vector mAvatars; + + int getLength() const override { return narrow(mAvatars.size()); } + LLSD getSingle(int index) const override + { + auto av = mAvatars[index]; + LLAvatarName av_name; + LLAvatarNameCache::get(av->getID(), &av_name); + LLVector3 region_pos = av->getCharacterPosition(); + return llsd::map("id", av->getID(), + "global_pos", ll_sd_from_vector3d(av->getPosGlobalFromAgent(region_pos)), + "region_pos", ll_sd_from_vector3(region_pos), + "name", av_name.getUserName()); + } +}; + +struct ObjResultSet : public LL::ResultSet +{ + ObjResultSet() : LL::ResultSet("nearby_objects") {} + std::vector mObjects; + + int getLength() const override { return narrow(mObjects.size()); } + LLSD getSingle(int index) const override + { + auto obj = mObjects[index]; + return llsd::map("id", obj->getID(), + "global_pos", ll_sd_from_vector3d(obj->getPositionGlobal()), + "region_pos", ll_sd_from_vector3(obj->getPositionRegion())); + } +}; + + +F32 get_search_radius(LLSD const& event_data) +{ + static LLCachedControl render_far_clip(gSavedSettings, "RenderFarClip", 64); + F32 dist = render_far_clip; + if (event_data.has("dist")) + { + dist = llclamp((F32)event_data["dist"].asReal(), 1, 512); + } + return dist * dist; +} + +void LLAgentListener::getNearbyAvatarsList(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + auto avresult = new AvResultSet; + + F32 radius = get_search_radius(event_data); + LLVector3d agent_pos = gAgent.getPositionGlobal(); + for (LLCharacter* character : LLCharacter::sInstances) + { + LLVOAvatar* avatar = (LLVOAvatar*)character; + if (!avatar->isDead() && !avatar->isControlAvatar() && !avatar->isSelf()) + { + if ((dist_vec_squared(avatar->getPositionGlobal(), agent_pos) <= radius)) + { + avresult->mAvatars.push_back(avatar); + } + } + } + response["result"] = avresult->getKeyLength(); +} + +void LLAgentListener::getNearbyObjectsList(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + auto objresult = new ObjResultSet; + + F32 radius = get_search_radius(event_data); + S32 num_objects = gObjectList.getNumObjects(); + LLVector3d agent_pos = gAgent.getPositionGlobal(); + for (S32 i = 0; i < num_objects; ++i) + { + LLViewerObject* object = gObjectList.getObject(i); + if (object && object->getVolume() && !object->isAttachment()) + { + if ((dist_vec_squared(object->getPositionGlobal(), agent_pos) <= radius)) + { + objresult->mObjects.push_back(object); + } + } + } + response["result"] = objresult->getKeyLength(); +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index 05724ff443..f1e85f0923 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -67,6 +67,10 @@ private: void stopAnimation(LLSD const &event_data); void getAnimationInfo(LLSD const &event_data); + void getID(LLSD const& event_data); + void getNearbyAvatarsList(LLSD const& event_data); + void getNearbyObjectsList(LLSD const& event_data); + LLViewerObject * findObjectClosestTo( const LLVector3 & position, bool sit_target = false ) const; private: diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 4a1132fe7e..70f3cdfffb 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -1,8 +1,26 @@ local leap = require 'leap' local mapargs = require 'mapargs' +local result_view = require 'result_view' + +local function result(keys) + local result_table = { + result=result_view(keys.result), + -- call result_table:close() to release result sets before garbage + -- collection or script completion + close = function(self) + result_view.close(keys.result[1]) + end + } + -- When the result_table is destroyed, close its result_views. + return LL.setdtor('LLAgent result', result_table, result_table.close) +end local LLAgent = {} +function LLAgent.getID() + return leap.request('LLAgent', {op = 'getID'}).id +end + function LLAgent.getRegionPosition() return leap.request('LLAgent', {op = 'getPosition'}).region end @@ -95,6 +113,24 @@ function LLAgent.requestStand() leap.send('LLAgent', {op = 'requestStand'}) end +-- Get the nearby avatars in a range of provided "dist", +-- if "dist" is not specified, "RenderFarClip" setting is used +-- reply will contain "result" table with following fields: +-- "id", "global_pos", "region_pos", "name" +function LLAgent.getNearbyAvatarsList(...) + local args = mapargs('dist', ...) + args.op = 'getNearbyAvatarsList' + return result(leap.request('LLAgent', args)) +end + +-- reply will contain "result" table with following fields: +-- "id", "global_pos", "region_pos" +function LLAgent.getNearbyObjectsList(...) + local args = mapargs('dist', ...) + args.op = 'getNearbyObjectsList' + return result(leap.request('LLAgent', args)) +end + -- *************************************************************************** -- Autopilot -- *************************************************************************** diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index cf2695917e..34f3fb75eb 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -222,8 +222,10 @@ function UI.hideFloater(floater_name) leap.send("LLFloaterReg", {op = "hideInstance", name = floater_name}) end -function UI.toggleFloater(floater_name) - leap.send("LLFloaterReg", {op = "toggleInstance", name = floater_name}) +function UI.toggleFloater(...) + local args = mapargs('name,key', ...) + args.op = 'toggleInstance' + leap.send("LLFloaterReg", args) end function UI.isFloaterVisible(floater_name) diff --git a/indra/newview/scripts/lua/test_nearby_avatars.lua b/indra/newview/scripts/lua/test_nearby_avatars.lua new file mode 100644 index 0000000000..c7fa686076 --- /dev/null +++ b/indra/newview/scripts/lua/test_nearby_avatars.lua @@ -0,0 +1,25 @@ +inspect = require 'inspect' +LLAgent = require 'LLAgent' + +-- Get the list of nearby avatars and print the info +local nearby_avatars = LLAgent.getNearbyAvatarsList() +if nearby_avatars.result.length == 0 then + print("No avatars nearby") +else + print("Nearby avatars:") + for _, av in pairs(nearby_avatars.result) do + print(av.name ..' ID: ' .. av.id ..' Global pos:' .. inspect(av.global_pos)) + end +end + +-- Get the list of nearby objects in 3m range and print the info +local nearby_objects = LLAgent.getNearbyObjectsList(3) +if nearby_objects.result.length == 0 then + print("No objects nearby") +else + print("Nearby objects:") + local pos={} + for _, obj in pairs(nearby_objects.result) do + print('ID: ' .. obj.id ..' Global pos:' .. inspect(obj.global_pos)) + end +end -- cgit v1.2.3 From 8a44149b481bf118d30a457514711c6ba4cf29b6 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 4 Oct 2024 18:47:34 +0300 Subject: remove trailing whitespace --- indra/newview/llagentlistener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index f00173dc13..f6029e7297 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -750,7 +750,7 @@ struct ObjResultSet : public LL::ResultSet LLSD getSingle(int index) const override { auto obj = mObjects[index]; - return llsd::map("id", obj->getID(), + return llsd::map("id", obj->getID(), "global_pos", ll_sd_from_vector3d(obj->getPositionGlobal()), "region_pos", ll_sd_from_vector3(obj->getPositionRegion())); } -- cgit v1.2.3 From d904d54b8fe9957cd7754eff59978ede6e8111c2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 4 Oct 2024 12:53:38 -0400 Subject: Use approximate equality for failing v3dmath test. Modernize lltut.h. Allow both F32 and F64 for ensure_approximately_equals() etc. Use std::string_view instead of const char* for message string to allow passing std::string as well. Use stringize() instead of explicit std::stringstream idiom. --- indra/llmath/tests/v3dmath_test.cpp | 13 ++--------- indra/test/lltut.h | 45 ++++++++++++++----------------------- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/indra/llmath/tests/v3dmath_test.cpp b/indra/llmath/tests/v3dmath_test.cpp index db08419012..36ad1067a2 100644 --- a/indra/llmath/tests/v3dmath_test.cpp +++ b/indra/llmath/tests/v3dmath_test.cpp @@ -504,14 +504,11 @@ namespace tut template<> template<> void v3dmath_object::test<24>() { -#if LL_WINDOWS && _MSC_VER < 1400 - skip("This fails on VS2003!"); -#else F64 x = 10., y = 20., z = -15.; F64 angle1, angle2; LLVector3d vec3Da(x,y,z), vec3Db(x,y,z); angle1 = angle_between(vec3Da, vec3Db); - ensure("1:angle_between: Fail ", (0 == angle1)); + ensure_approximately_equals_range("1:angle_between: Fail ", angle1, 0., 1.5e-8); F64 x1 = -1., y1 = -20., z1 = -1.; vec3Da.clearVec(); vec3Da.setVec(x1,y1,z1); @@ -520,12 +517,6 @@ namespace tut vec3Da.normVec(); F64 angle = vec3Db*vec3Da; angle = acos(angle); -#if LL_WINDOWS && _MSC_VER > 1900 - skip("This fails on VS2017!"); -#else - ensure("2:angle_between: Fail ", (angle == angle2)); -#endif - -#endif + ensure_equals("2:angle_between: Fail ", angle, angle2); } } diff --git a/indra/test/lltut.h b/indra/test/lltut.h index fbf60444be..3855eae7a2 100644 --- a/indra/test/lltut.h +++ b/indra/test/lltut.h @@ -30,8 +30,10 @@ #define LL_LLTUT_H #include "is_approx_equal_fraction.h" // instead of llmath.h +#include "stringize.h" #include #include +#include #include class LLDate; @@ -89,49 +91,38 @@ namespace tut // The functions BELOW this point actually consume tut.hpp functionality. namespace tut { - inline void ensure_approximately_equals(const char* msg, F64 actual, F64 expected, U32 frac_bits) + template // replace with C++20 floating-point concept + inline void ensure_approximately_equals(std::string_view msg, F actual, F expected, U32 frac_bits) { if(!is_approx_equal_fraction(actual, expected, frac_bits)) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal actual: ", + actual, " expected: ", expected)); } } - inline void ensure_approximately_equals(const char* msg, F32 actual, F32 expected, U32 frac_bits) + template // replace with C++20 floating-point concept + inline void ensure_approximately_equals(F actual, F expected, U32 frac_bits) { - if(!is_approx_equal_fraction(actual, expected, frac_bits)) - { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); - } - } - - inline void ensure_approximately_equals(F32 actual, F32 expected, U32 frac_bits) - { - ensure_approximately_equals(NULL, actual, expected, frac_bits); + ensure_approximately_equals("", actual, expected, frac_bits); } - inline void ensure_approximately_equals_range(const char *msg, F32 actual, F32 expected, F32 delta) + template // replace with C++20 floating-point concept + inline void ensure_approximately_equals_range(std::string_view msg, F actual, F expected, F delta) { if (fabs(actual-expected)>delta) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected << " tolerance: " << delta; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal actual: ", + actual, " expected: ", expected, " tolerance: ", delta)); } } - inline void ensure_memory_matches(const char* msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len) + inline void ensure_memory_matches(std::string_view msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len) { if((expected_len != actual_len) || (std::memcmp(actual, expected, actual_len) != 0)) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal"; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal")); } } @@ -141,13 +132,11 @@ namespace tut } template - void ensure_not_equals(const char* msg,const Q& actual,const T& expected) + void ensure_not_equals(std::string_view msg,const Q& actual,const T& expected) { if( expected == actual ) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "both equal " << expected; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "both equal ", expected)); } } -- cgit v1.2.3 From 0e86bebcfc80259476654190880f9bfd5b35934e Mon Sep 17 00:00:00 2001 From: Brad Linden <46733234+brad-linden@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:46:33 -0700 Subject: #2650 Add UI controls for debug settings: RenderTonemapMix RenderTonemapType (#2787) Co-authored-by: Maxim Nikolenko --- .../en/floater_preferences_graphics_advanced.xml | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml index 1deb81175e..8bb15d1d3a 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml @@ -943,6 +943,57 @@ width="260"> + + + Tone Mapper: + + + + + + + + Date: Fri, 4 Oct 2024 14:16:04 -0700 Subject: cherry pick secondlife/viewer#912 BugSplat Crash 1412267: nvoglv64+0xadcd00 (#2785) * secondlife/viewer#912 BugSplat Crash 1412267: nvoglv64+0xadcd00 * fix cherry-pick merge breakage. * Fix signed/unsigned error --------- Co-authored-by: Alexander Gavriliuk --- indra/llprimitive/lldaeloader.cpp | 40 ++++----- indra/llprimitive/llmodel.cpp | 16 ++-- indra/llprimitive/llmodel.h | 2 +- indra/llprimitive/llmodelloader.cpp | 25 ++---- indra/llprimitive/llmodelloader.h | 41 +++++---- indra/newview/llmodelpreview.cpp | 164 +++++++++++++++++++----------------- 6 files changed, 140 insertions(+), 148 deletions(-) diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index f286bff353..7fa4230237 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -1115,19 +1115,17 @@ bool LLDAELoader::OpenFile(const std::string& filename) if (skin) { - domGeometry* geom = daeSafeCast(skin->getSource().getElement()); - - if (geom) + if (domGeometry* geom = daeSafeCast(skin->getSource().getElement())) { - domMesh* mesh = geom->getMesh(); - if (mesh) + if (domMesh* mesh = geom->getMesh()) { - std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin(); - while (i != mModelsMap[mesh].end()) + dae_model_map::const_iterator it = mModelsMap.find(mesh); + if (it != mModelsMap.end()) { - LLPointer mdl = *i; - LLDAELoader::processDomModel(mdl, &dae, root, mesh, skin); - i++; + for (const LLPointer& model : it->second) + { + LLDAELoader::processDomModel(model, &dae, root, mesh, skin); + } } } } @@ -1297,6 +1295,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do } } else + { //Has one or more skeletons for (std::vector::iterator skel_it = skeletons.begin(); skel_it != skeletons.end(); ++skel_it) @@ -1381,6 +1380,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do } }//got skeleton? } + } domSkin::domJoints* joints = skin->getJoints(); @@ -1681,7 +1681,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do materials[model->mMaterialList[i]] = LLImportMaterial(); } mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials)); - stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); + stretch_extents(model, transformation); } } @@ -2074,21 +2074,14 @@ void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* da mTransform.condition(); } - domInstance_geometry* instance_geo = daeSafeCast(element); - if (instance_geo) + if (domInstance_geometry* instance_geo = daeSafeCast(element)) { - domGeometry* geo = daeSafeCast(instance_geo->getUrl().getElement()); - if (geo) + if (domGeometry* geo = daeSafeCast(instance_geo->getUrl().getElement())) { - domMesh* mesh = daeSafeCast(geo->getDescendant(daeElement::matchType(domMesh::ID()))); - if (mesh) + if (domMesh* mesh = daeSafeCast(geo->getDescendant(daeElement::matchType(domMesh::ID())))) { - - std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin(); - while (i != mModelsMap[mesh].end()) + for (LLModel* model : mModelsMap.find(mesh)->second) { - LLModel* model = *i; - LLMatrix4 transformation = mTransform; if (mTransform.determinant() < 0) @@ -2159,8 +2152,7 @@ void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* da } mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials)); - stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); - i++; + stretch_extents(model, transformation); } } } diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 9908a155f2..4e3e49ec9f 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -91,19 +91,15 @@ std::string LLModel::getStatusString(U32 status) } -void LLModel::offsetMesh( const LLVector3& pivotPoint ) +void LLModel::offsetMesh(const LLVector3& pivotPoint) { - LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] ); + LLVector4a pivot(pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ]); - for (std::vector::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); ) + for (LLVolumeFace& face : mVolumeFaces) { - std::vector:: iterator currentFaceIt = faceIt++; - LLVolumeFace& face = *currentFaceIt; - LLVector4a *pos = (LLVector4a*) face.mPositions; - - for (S32 i=0; i LLModelLoader::sActiveLoaderList; -void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, bool& first_transform) +static void stretch_extents(const LLModel* model, const LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, bool& first_transform) { LLVector4a box[] = { @@ -84,19 +84,19 @@ void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4 } } -void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, bool& first_transform) +void LLModelLoader::stretch_extents(const LLModel* model, const LLMatrix4& mat) { LLVector4a mina, maxa; LLMatrix4a mata; mata.loadu(mat); - mina.load3(min.mV); - maxa.load3(max.mV); + mina.load3(mExtents[0].mV); + maxa.load3(mExtents[1].mV); - stretch_extents(model, mata, mina, maxa, first_transform); + ::stretch_extents(model, mata, mina, maxa, mFirstTransform); - min.set(mina.getF32ptr()); - max.set(maxa.getF32ptr()); + mExtents[0].set(mina.getF32ptr()); + mExtents[1].set(maxa.getF32ptr()); } //----------------------------------------------------------------------------- @@ -291,14 +291,7 @@ bool LLModelLoader::loadFromSLM(const std::string& filename) { if (idx >= model[lod].size()) { - if (model[lod].size()) - { - instance_list[i].mLOD[lod] = model[lod][0]; - } - else - { - instance_list[i].mLOD[lod] = NULL; - } + instance_list[i].mLOD[lod] = model[lod].front(); continue; } @@ -341,7 +334,7 @@ bool LLModelLoader::loadFromSLM(const std::string& filename) { LLModelInstance& cur_instance = instance_list[i]; mScene[cur_instance.mTransform].push_back(cur_instance); - stretch_extents(cur_instance.mModel, cur_instance.mTransform, mExtents[0], mExtents[1], mFirstTransform); + stretch_extents(cur_instance.mModel, cur_instance.mTransform); } setLoadState( DONE ); diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h index 83bd2f5bbd..530e61e2b8 100644 --- a/indra/llprimitive/llmodelloader.h +++ b/indra/llprimitive/llmodelloader.h @@ -34,13 +34,13 @@ class LLJoint; -typedef std::map JointTransformMap; -typedef std::map::iterator JointTransformMapIt; -typedef std::map JointMap; -typedef std::deque JointNameSet; +typedef std::map JointTransformMap; +typedef std::map::iterator JointTransformMapIt; +typedef std::map JointMap; +typedef std::deque JointNameSet; const S32 SLM_SUPPORTED_VERSION = 3; -const S32 NUM_LOD = 4; +const S32 NUM_LOD = 4; const U32 LEGACY_RIG_OK = 0; const U32 LEGACY_RIG_FLAG_TOO_MANY_JOINTS = 1; @@ -50,32 +50,32 @@ class LLModelLoader : public LLThread { public: - typedef std::map material_map; - typedef std::vector > model_list; - typedef std::vector model_instance_list; - typedef std::map scene; + typedef std::map material_map; + typedef std::vector> model_list; + typedef std::vector model_instance_list; + typedef std::map scene; // Callback with loaded model data and loaded LoD // - typedef boost::function load_callback_t; + typedef boost::function load_callback_t; // Function to provide joint lookup by name // (within preview avi skeleton, for example) // - typedef boost::function joint_lookup_func_t; + typedef boost::function joint_lookup_func_t; // Func to load and associate material with all it's textures, // returned value is the number of textures loaded // intentionally non-const so func can modify material to // store platform-specific data // - typedef boost::function texture_load_func_t; + typedef boost::function texture_load_func_t; // Callback to inform client of state changes // during loading process (errors will be reported // as state changes here as well) // - typedef boost::function state_callback_t; + typedef boost::function state_callback_t; typedef enum { @@ -136,7 +136,7 @@ public: JointNameSet& jointsFromNodes, JointMap& legalJointNamesMap, U32 maxJointsPerMesh); - virtual ~LLModelLoader() ; + virtual ~LLModelLoader(); virtual void setNoNormalize() { mNoNormalize = true; } virtual void setNoOptimize() { mNoOptimize = true; } @@ -156,13 +156,13 @@ public: bool loadFromSLM(const std::string& filename); void loadModelCallback(); - void loadTextures() ; //called in the main thread. + void loadTextures() ; // called in the main thread. void setLoadState(U32 state); + void stretch_extents(const LLModel* model, const LLMatrix4& mat); - - S32 mNumOfFetchingTextures ; //updated in the main thread - bool areTexturesReady() { return !mNumOfFetchingTextures; } //called in the main thread. + S32 mNumOfFetchingTextures ; // updated in the main thread + bool areTexturesReady() { return !mNumOfFetchingTextures; } // called in the main thread. bool verifyCount( int expected, int result ); @@ -212,10 +212,7 @@ protected: LLSD mWarningsArray; // preview floater will pull logs from here static std::list sActiveLoaderList; - static bool isAlive(LLModelLoader* loader) ; + static bool isAlive(LLModelLoader* loader); }; -class LLMatrix4a; -void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, bool& first_transform); -void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, bool& first_transform); #endif // LL_LLMODELLOADER_H diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index ef09cfa55b..1e7da126b0 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -134,25 +134,17 @@ std::string getLodSuffix(S32 lod) void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut) { - LLModelLoader::scene::iterator base_iter = scene.begin(); - bool found = false; - while (!found && (base_iter != scene.end())) + for (auto scene_iter = scene.begin(); scene_iter != scene.end(); scene_iter++) { - matOut = base_iter->first; - - LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin(); - while (!found && (base_instance_iter != base_iter->second.end())) + for (auto model_iter = scene_iter->second.begin(); model_iter != scene_iter->second.end(); model_iter++) { - LLModelInstance& base_instance = *base_instance_iter++; - LLModel* base_model = base_instance.mModel; - - if (base_model && (base_model->mLabel == name_to_match)) + if (model_iter->mModel && (model_iter->mModel->mLabel == name_to_match)) { - baseModelOut = base_model; + baseModelOut = model_iter->mModel; + matOut = scene_iter->first; return; } } - base_iter++; } } @@ -212,9 +204,12 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) LLModelPreview::~LLModelPreview() { + LLMutexLock lock(this); + if (mModelLoader) { mModelLoader->shutdown(); + mModelLoader = NULL; } if (mPreviewAvatar) @@ -262,7 +257,7 @@ void LLModelPreview::updateDimentionsAndOffsets() accounted.insert(instance.mModel); // update instance skin info for each lods pelvisZoffset - for (int j = 0; jfirst; @@ -322,9 +317,9 @@ void LLModelPreview::rebuildUploadData() mat *= scale_mat; - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();) + for (auto model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) { // for each instance with said transform applied - LLModelInstance instance = *model_iter++; + LLModelInstance instance = *model_iter; LLModel* base_model = instance.mModel; @@ -910,7 +905,7 @@ void LLModelPreview::clearIncompatible(S32 lod) { mBaseModel = mModel[lod]; mBaseScene = mScene[lod]; - mVertexBuffer[5].clear(); + mVertexBuffer[LLModel::NUM_LODS].clear(); replaced_base_model = true; } } @@ -1132,7 +1127,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod) mBaseModel = mModel[loaded_lod]; mBaseScene = mScene[loaded_lod]; - mVertexBuffer[5].clear(); + mVertexBuffer[LLModel::NUM_LODS].clear(); } else { @@ -1248,7 +1243,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod) { if (!mBaseModel.empty()) { - const std::string& model_name = mBaseModel[0]->getName(); + std::string model_name = mBaseModel.front()->getName(); LLLineEditor* description_form = mFMP->getChild("description_form"); if (description_form->getText().empty()) { @@ -1269,6 +1264,8 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod) void LLModelPreview::resetPreviewTarget() { + LLMutexLock lock(this); + if (mModelLoader) { mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; @@ -1314,7 +1311,7 @@ void LLModelPreview::generateNormals() (*it)->generateNormals(angle_cutoff); } - mVertexBuffer[5].clear(); + mVertexBuffer[LLModel::NUM_LODS].clear(); } bool perform_copy = mModelFacesCopy[which_lod].empty(); @@ -2156,7 +2153,7 @@ void LLModelPreview::updateStatusMessages() S32 total_verts[LLModel::NUM_LODS]; S32 total_submeshes[LLModel::NUM_LODS]; - for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + for (U32 i = 0; i < LLModel::NUM_LODS; i++) { total_tris[i] = 0; total_verts[i] = 0; @@ -2460,12 +2457,16 @@ void LLModelPreview::updateStatusMessages() } } - if (mModelNoErrors && mModelLoader) + if (mModelNoErrors) { - if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) + LLMutexLock lock(this); + if (mModelLoader) { - // Some textures are still loading, prevent upload until they are done - mModelNoErrors = false; + if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) + { + // Some textures are still loading, prevent upload until they are done + mModelNoErrors = false; + } } } @@ -2794,10 +2795,10 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) { LLModelLoader::model_list* model = NULL; - if (lod < 0 || lod > 4) + if (lod < 0 || lod >= LLModel::NUM_LODS) { model = &mBaseModel; - lod = 5; + lod = LLModel::NUM_LODS; } else { @@ -3034,8 +3035,9 @@ void LLModelPreview::loadedCallback( S32 lod, void* opaque) { - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview && !LLModelPreview::sIgnoreLoadedCallback) + LLModelPreview* pPreview = static_cast(opaque); + LLMutexLock lock(pPreview); + if (pPreview && pPreview->mModelLoader && !LLModelPreview::sIgnoreLoadedCallback) { // Load loader's warnings into floater's log tab const LLSD out = pPreview->mModelLoader->logOut(); @@ -3056,7 +3058,9 @@ void LLModelPreview::loadedCallback( } const LLVOAvatar* avatarp = pPreview->getPreviewAvatar(); - if (avatarp) { // set up ground plane for possible rendering + if (avatarp && avatarp->mRoot && avatarp->mDrawable) + { + // set up ground plane for possible rendering const LLVector3 root_pos = avatarp->mRoot->getPosition(); const LLVector4a* ext = avatarp->mDrawable->getSpatialExtents(); const LLVector4a min = ext[0], max = ext[1]; @@ -3200,12 +3204,12 @@ bool LLModelPreview::render() LLMutexLock lock(this); mNeedsUpdate = false; - bool edges = mViewOption["show_edges"]; - bool joint_overrides = mViewOption["show_joint_overrides"]; - bool joint_positions = mViewOption["show_joint_positions"]; - bool skin_weight = mViewOption["show_skin_weight"]; - bool textures = mViewOption["show_textures"]; - bool physics = mViewOption["show_physics"]; + bool show_edges = mViewOption["show_edges"]; + bool show_joint_overrides = mViewOption["show_joint_overrides"]; + bool show_joint_positions = mViewOption["show_joint_positions"]; + bool show_skin_weight = mViewOption["show_skin_weight"]; + bool show_textures = mViewOption["show_textures"]; + bool show_physics = mViewOption["show_physics"]; S32 width = getWidth(); S32 height = getHeight(); @@ -3282,15 +3286,15 @@ bool LLModelPreview::render() fmp->childSetValue("upload_skin", true); mFirstSkinUpdate = false; upload_skin = true; - skin_weight = true; + show_skin_weight = true; mViewOption["show_skin_weight"] = true; } fmp->enableViewOption("show_skin_weight"); - fmp->setViewOptionEnabled("show_joint_overrides", skin_weight); - fmp->setViewOptionEnabled("show_joint_positions", skin_weight); + fmp->setViewOptionEnabled("show_joint_overrides", show_skin_weight); + fmp->setViewOptionEnabled("show_joint_positions", show_skin_weight); mFMP->childEnable("upload_skin"); - mFMP->childSetValue("show_skin_weight", skin_weight); + mFMP->childSetValue("show_skin_weight", show_skin_weight); } else if ((flags & LEGACY_RIG_FLAG_TOO_MANY_JOINTS) > 0) @@ -3313,11 +3317,12 @@ bool LLModelPreview::render() fmp->disableViewOption("show_joint_overrides"); fmp->disableViewOption("show_joint_positions"); - skin_weight = false; + show_skin_weight = false; mFMP->childSetValue("show_skin_weight", false); - fmp->setViewOptionEnabled("show_skin_weight", skin_weight); + fmp->setViewOptionEnabled("show_skin_weight", show_skin_weight); } } + //if (this) return TRUE; if (upload_skin && !has_skin_weights) { //can't upload skin weights if model has no skin weights @@ -3360,7 +3365,7 @@ bool LLModelPreview::render() mFMP->childSetEnabled("upload_joints", upload_skin); } - F32 explode = (F32)mFMP->childGetValue("physics_explode").asReal(); + F32 physics_explode = (F32)mFMP->childGetValue("physics_explode").asReal(); LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview @@ -3380,7 +3385,7 @@ bool LLModelPreview::render() F32 z_near = 0.001f; F32 z_far = mCameraDistance*10.0f + mPreviewScale.magVec() + mCameraOffset.magVec(); - if (skin_weight) + if (show_skin_weight) { target_pos = getPreviewAvatar()->getPositionAgent() + offset; z_near = 0.01f; @@ -3390,7 +3395,7 @@ bool LLModelPreview::render() refresh(); } - gObjectPreviewProgram.bind(skin_weight); + gObjectPreviewProgram.bind(show_skin_weight); gGL.loadIdentity(); gPipeline.enableLightsPreview(); @@ -3399,7 +3404,7 @@ bool LLModelPreview::render() LLQuaternion(mCameraYaw, LLVector3::z_axis); LLQuaternion av_rot = camera_rot; - F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; + F32 camera_distance = show_skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; LLViewerCamera::getInstance()->setOriginAndLookAt( target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera LLVector3::z_axis, // up @@ -3415,9 +3420,9 @@ bool LLModelPreview::render() gGL.pushMatrix(); gGL.color4fv(PREVIEW_EDGE_COL.mV); - if (!mBaseModel.empty() && mVertexBuffer[5].empty()) + if (!mBaseModel.empty() && mVertexBuffer[LLModel::NUM_LODS].empty()) { - genBuffers(-1, skin_weight); + genBuffers(-1, show_skin_weight); //genBuffers(3); } @@ -3432,7 +3437,7 @@ bool LLModelPreview::render() if (!vb_vec.empty()) { const LLVertexBuffer* buff = vb_vec[0]; - regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; + regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != show_skin_weight; } else { @@ -3443,15 +3448,15 @@ bool LLModelPreview::render() if (regen) { - genBuffers(mPreviewLOD, skin_weight); + genBuffers(mPreviewLOD, show_skin_weight); } - if (physics && mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + if (show_physics && mVertexBuffer[LLModel::LOD_PHYSICS].empty()) { genBuffers(LLModel::LOD_PHYSICS, false); } - if (!skin_weight) + if (!show_skin_weight) { for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) { @@ -3473,11 +3478,7 @@ bool LLModelPreview::render() auto num_models = mVertexBuffer[mPreviewLOD][model].size(); for (size_t i = 0; i < num_models; ++i) { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - buffer->setBuffer(); - - if (textures) + if (show_textures) { auto materialCnt = instance.mModel->mMaterialList.size(); if (i < materialCnt) @@ -3501,10 +3502,16 @@ bool LLModelPreview::render() gGL.diffuseColor4fv(PREVIEW_BASE_COL.mV); } + // Zero this variable for an obligatory buffer initialization + // See https://github.com/secondlife/viewer/issues/912 + LLVertexBuffer::sGLRenderBuffer = 0; + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + buffer->setBuffer(); buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); - if (edges) + if (show_edges) { glLineWidth(PREVIEW_EDGE_WIDTH); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); @@ -3517,7 +3524,7 @@ bool LLModelPreview::render() gGL.popMatrix(); } - if (physics) + if (show_physics) { glClear(GL_DEPTH_BUFFER_BIT); @@ -3583,12 +3590,12 @@ bool LLModelPreview::render() for (U32 i = 0; i < physics.mMesh.size(); ++i) { - if (explode > 0.f) + if (physics_explode > 0.f) { gGL.pushMatrix(); LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters; - offset *= explode; + offset *= physics_explode; gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); } @@ -3603,7 +3610,7 @@ bool LLModelPreview::render() gGL.diffuseColor4ubv(hull_colors[i].mV); LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions); - if (explode > 0.f) + if (physics_explode > 0.f) { gGL.popMatrix(); } @@ -3618,14 +3625,17 @@ bool LLModelPreview::render() if (render_mesh) { auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); - if (pass > 0){ + if (pass > 0) + { for (size_t i = 0; i < num_models; ++i) { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.diffuseColor4fv(PREVIEW_PSYH_FILL_COL.mV); + // Zero this variable for an obligatory buffer initialization + // See https://github.com/secondlife/viewer/issues/912 + LLVertexBuffer::sGLRenderBuffer = 0; + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; buffer->setBuffer(); buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); @@ -3685,10 +3695,11 @@ bool LLModelPreview::render() auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); for (size_t v = 0; v < num_models; ++v) { + // Zero this variable for an obligatory buffer initialization + // See https://github.com/secondlife/viewer/issues/912 + LLVertexBuffer::sGLRenderBuffer = 0; LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v]; - buffer->setBuffer(); - LLStrider pos_strider; buffer->getVertexStrider(pos_strider, 0); LLVector4a* pos = (LLVector4a*)pos_strider.get(); @@ -3752,7 +3763,7 @@ bool LLModelPreview::render() U32 joint_count = LLSkinningUtil::getMeshJointCount(skin); auto bind_count = skin->mAlternateBindMatrix.size(); - if (joint_overrides + if (show_joint_overrides && bind_count > 0 && joint_count == bind_count) { @@ -3795,16 +3806,15 @@ bool LLModelPreview::render() } } - for (U32 i = 0, e = static_cast(mVertexBuffer[mPreviewLOD][model].size()); i < e; ++i) + std::size_t size = mVertexBuffer[mPreviewLOD][model].size(); + for (U32 i = 0; i < size; ++i) { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - model->mSkinInfo.updateHash(); LLRenderPass::uploadMatrixPalette(mPreviewAvatar, &model->mSkinInfo); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - if (textures) + if (show_textures) { auto materialCnt = instance.mModel->mMaterialList.size(); if (i < materialCnt) @@ -3828,10 +3838,14 @@ bool LLModelPreview::render() gGL.diffuseColor4fv(PREVIEW_BASE_COL.mV); } + // Zero this variable for an obligatory buffer initialization + // See https://github.com/secondlife/viewer/issues/912 + LLVertexBuffer::sGLRenderBuffer = 0; + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; buffer->setBuffer(); buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - if (edges) + if (show_edges) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); @@ -3846,7 +3860,7 @@ bool LLModelPreview::render() } } - if (joint_positions) + if (show_joint_positions) { LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; if (shader) -- cgit v1.2.3 From 85b7210fb9f849633b27a4b4631c42ae7dba78f2 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 4 Oct 2024 21:11:56 +0300 Subject: viewer#2741 Sligtly better logging for a crash --- indra/llrender/llimagegl.cpp | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 67b4ada62f..abbf90bf59 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1323,12 +1323,16 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { //GL_ALPHA is deprecated, convert to RGBA if (pixels != nullptr) { - scratch.reset(new(std::nothrow) U32[width * height]); - if (!scratch) + try + { + scratch.reset(new U32[width * height]); + } + catch (std::bad_alloc) { LLError::LLUserWarningMsg::showOutOfMemory(); LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height << LL_ENDL; + << " bytes for a manual image W" << width << " H" << height + << " Pixformat: GL_ALPHA, pixtype: GL_UNSIGNED_BYTE" << LL_ENDL; } U32 pixel_count = (U32)(width * height); @@ -1350,12 +1354,16 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA if (pixels != nullptr) { - scratch.reset(new(std::nothrow) U32[width * height]); - if (!scratch) + try + { + scratch.reset(new U32[width * height]); + } + catch (std::bad_alloc) { LLError::LLUserWarningMsg::showOutOfMemory(); LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height << LL_ENDL; + << " bytes for a manual image W" << width << " H" << height + << " Pixformat: GL_LUMINANCE_ALPHA, pixtype: GL_UNSIGNED_BYTE" << LL_ENDL; } U32 pixel_count = (U32)(width * height); @@ -1380,12 +1388,16 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { //GL_LUMINANCE_ALPHA is deprecated, convert to RGB if (pixels != nullptr) { - scratch.reset(new(std::nothrow) U32[width * height]); - if (!scratch) + try + { + scratch.reset(new U32[width * height]); + } + catch (std::bad_alloc) { LLError::LLUserWarningMsg::showOutOfMemory(); LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height << LL_ENDL; + << " bytes for a manual image W" << width << " H" << height + << " Pixformat: GL_LUMINANCE, pixtype: GL_UNSIGNED_BYTE" << LL_ENDL; } U32 pixel_count = (U32)(width * height); -- cgit v1.2.3 From b71343e827fe8b1e2dba6978b67bb3112944c674 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Sat, 5 Oct 2024 11:56:02 +0300 Subject: viewer#2443 voice dot indicator should be visible by default --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llvoicevisualizer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 7c9cfb94fa..13ec35fa07 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13442,7 +13442,7 @@ Type Boolean Value - 0 + 1 WarningsAsChat diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp index 7691ac54f3..c5704982b8 100644 --- a/indra/newview/llvoicevisualizer.cpp +++ b/indra/newview/llvoicevisualizer.cpp @@ -337,7 +337,7 @@ void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah ) //--------------------------------------------------- void LLVoiceVisualizer::render() { - static LLCachedControl show_visualizer(gSavedSettings, "VoiceVisualizerEnabled", false); + static LLCachedControl show_visualizer(gSavedSettings, "VoiceVisualizerEnabled", true); if (!mVoiceEnabled || !show_visualizer) { return; -- cgit v1.2.3 From ccc7fffb22b347149bf807b9d92677e5616c5017 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 8 Oct 2024 15:40:12 +0300 Subject: Lua api to get avatar screen position --- indra/newview/llagentlistener.cpp | 36 +++++++++++++++++++++- indra/newview/llagentlistener.h | 1 + indra/newview/scripts/lua/require/LLAgent.lua | 9 ++++++ indra/newview/scripts/lua/test_screen_position.lua | 8 +++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 indra/newview/scripts/lua/test_screen_position.lua diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index f6029e7297..9255a850e5 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -49,7 +49,7 @@ #include "llsdutil_math.h" #include "lltoolgrab.h" #include "llhudeffectlookat.h" -#include "llagentcamera.h" +#include "llviewercamera.h" #include "resultset.h" #include @@ -199,6 +199,13 @@ LLAgentListener::LLAgentListener(LLAgent &agent) "reply contains \"result\" table with \"id\", \"global_pos\", \"region_pos\" fields", &LLAgentListener::getNearbyObjectsList, llsd::map("reply", LLSD())); + + add("getAgentScreenPos", + "Return screen position of the [\"avatar_id\"] avatar or own avatar if not specified\n" + "reply contains \"x\", \"y\" coordinates and \"onscreen\" flag to indicate if it's actually in within the current window\n" + "avatar render position is used as the point", + &LLAgentListener::getAgentScreenPos, + llsd::map("reply", LLSD())); } void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -810,3 +817,30 @@ void LLAgentListener::getNearbyObjectsList(LLSD const& event_data) } response["result"] = objresult->getKeyLength(); } + +void LLAgentListener::getAgentScreenPos(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + LLVector3 render_pos; + if (event_data.has("avatar_id")) + { + LLUUID avatar_id(event_data["avatar_id"]); + for (LLCharacter* character : LLCharacter::sInstances) + { + LLVOAvatar* avatar = (LLVOAvatar*)character; + if (!avatar->isDead() && (avatar->getID() == avatar_id)) + { + render_pos = avatar->getRenderPosition(); + break; + } + } + } + else if (gAgentAvatarp.notNull() && gAgentAvatarp->isValid()) + { + render_pos = gAgentAvatarp->getRenderPosition(); + } + LLCoordGL screen_pos; + response["onscreen"] = LLViewerCamera::getInstance()->projectPosAgentToScreen(render_pos, screen_pos, false); + response["x"] = screen_pos.mX; + response["y"] = screen_pos.mY; +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index f1e85f0923..8801c9f7ea 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -70,6 +70,7 @@ private: void getID(LLSD const& event_data); void getNearbyAvatarsList(LLSD const& event_data); void getNearbyObjectsList(LLSD const& event_data); + void getAgentScreenPos(LLSD const& event_data); LLViewerObject * findObjectClosestTo( const LLVector3 & position, bool sit_target = false ) const; diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 70f3cdfffb..b2be69dcdf 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -131,6 +131,15 @@ function LLAgent.getNearbyObjectsList(...) return result(leap.request('LLAgent', args)) end +-- Get screen position of your own avatar or any other (if "avatar_id is specified) +-- reply contains "x", "y" coordinates and "onscreen" flag to indicate if it's actually in within the current window +-- avatar render position is used as the point +function LLAgent.getAgentScreenPos(...) + local args = mapargs('avatar_id', ...) + args.op = 'getAgentScreenPos' + return leap.request('LLAgent', args) +end + -- *************************************************************************** -- Autopilot -- *************************************************************************** diff --git a/indra/newview/scripts/lua/test_screen_position.lua b/indra/newview/scripts/lua/test_screen_position.lua new file mode 100644 index 0000000000..94d57339b1 --- /dev/null +++ b/indra/newview/scripts/lua/test_screen_position.lua @@ -0,0 +1,8 @@ +LLAgent = require 'LLAgent' + +local screen_pos = LLAgent.getAgentScreenPos() +if screen_pos.onscreen then + print("Avatar screen coordinates X: " .. screen_pos.x .. " Y: " .. screen_pos.y) +else + print("Avatar is not on the screen") +end -- cgit v1.2.3 From a301fb2876835f799317f8a011f368eaec4894d6 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Tue, 8 Oct 2024 18:22:01 +0300 Subject: Follow-up test fix --- indra/test/lltut.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/test/lltut.h b/indra/test/lltut.h index 3855eae7a2..986bdd0619 100644 --- a/indra/test/lltut.h +++ b/indra/test/lltut.h @@ -143,7 +143,7 @@ namespace tut template void ensure_not_equals(const Q& actual,const T& expected) { - ensure_not_equals(NULL, actual, expected); + ensure_not_equals("", actual, expected); } } -- cgit v1.2.3 From 3091985d31e1ccf9aba159dec4f2d5ea31723ce0 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 10 Oct 2024 16:03:48 +0300 Subject: Update test_animation.lua demo script --- indra/newview/llinventorylistener.cpp | 6 ++++-- indra/newview/scripts/lua/test_animation.lua | 18 +++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index 79726c3e0b..88b07c0b0b 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -116,7 +116,8 @@ struct CatResultSet: public LL::ResultSet LLSD getSingle(int index) const override { auto cat = mCategories[index]; - return llsd::map("name", cat->getName(), + return llsd::map("id", cat->getUUID(), + "name", cat->getName(), "parent_id", cat->getParentUUID(), "type", LLFolderType::lookup(cat->getPreferredType())); } @@ -133,7 +134,8 @@ struct ItemResultSet: public LL::ResultSet LLSD getSingle(int index) const override { auto item = mItems[index]; - return llsd::map("name", item->getName(), + return llsd::map("id", item->getUUID(), + "name", item->getName(), "parent_id", item->getParentUUID(), "desc", item->getDescription(), "inv_type", LLInventoryType::lookup(item->getInventoryType()), diff --git a/indra/newview/scripts/lua/test_animation.lua b/indra/newview/scripts/lua/test_animation.lua index 37e7254a6c..ae5eabe148 100644 --- a/indra/newview/scripts/lua/test_animation.lua +++ b/indra/newview/scripts/lua/test_animation.lua @@ -7,8 +7,8 @@ animations_id = LLInventory.getBasicFolderID('animatn') anims = LLInventory.collectDescendentsIf{folder_id=animations_id, type="animatn"}.items local anim_ids = {} -for key in pairs(anims) do - table.insert(anim_ids, key) +for _, key in pairs(anims) do + table.insert(anim_ids, {id = key.id, name = key.name}) end if #anim_ids == 0 then @@ -16,17 +16,17 @@ if #anim_ids == 0 then else -- Start playing a random animation math.randomseed(os.time()) - local random_id = anim_ids[math.random(#anim_ids)] - local anim_info = LLAgent.getAnimationInfo(random_id) + local random_anim = anim_ids[math.random(#anim_ids)] + local anim_info = LLAgent.getAnimationInfo(random_anim.id) - print("Starting animation locally: " .. anims[random_id].name) - print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration))) - LLAgent.playAnimation{item_id=random_id} + print("Starting animation locally: " .. random_anim.name) + print("Loop: " .. tostring(anim_info.is_loop) .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration))) + LLAgent.playAnimation{item_id=random_anim.id} -- Stop animation after 3 sec if it's looped or longer than 3 sec - if anim_info.is_loop == 1 or anim_info.duration > 3 then + if anim_info.is_loop or anim_info.duration > 3 then LL.sleep(3) print("Stop animation.") - LLAgent.stopAnimation(random_id) + LLAgent.stopAnimation(random_anim.id) end end -- cgit v1.2.3 From 3f4c977bc9c4604f0216f0463bd609c615b6c0b7 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 11 Oct 2024 17:55:27 +0300 Subject: Update demo scripts --- indra/newview/llviewerwindowlistener.cpp | 9 ++++++--- indra/newview/scripts/lua/require/UI.lua | 2 +- indra/newview/scripts/lua/test_LLChatListener.lua | 9 +++++++-- indra/newview/scripts/lua/test_luafloater_demo.lua | 2 +- indra/newview/scripts/lua/test_toolbars.lua | 4 ++-- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp index 52f413792a..6ce5e68642 100644 --- a/indra/newview/llviewerwindowlistener.cpp +++ b/indra/newview/llviewerwindowlistener.cpp @@ -35,6 +35,7 @@ // std headers // external library headers // other Linden headers +#include "llcallbacklist.h" #include "llviewerwindow.h" LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): @@ -46,8 +47,7 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): add("saveSnapshot", "Save screenshot: [\"filename\"] (extension may be specified: bmp, jpeg, png)\n" "[\"width\"], [\"height\"], [\"showui\"], [\"showhud\"], [\"rebuild\"], [\"type\"]\n" - "type: \"COLOR\", \"DEPTH\"\n" - "Post on [\"reply\"] an event containing [\"result\"]", + "type: \"COLOR\", \"DEPTH\"\n", &LLViewerWindowListener::saveSnapshot, llsd::map("filename", LLSD::String(), "reply", LLSD())); add("requestReshape", @@ -117,7 +117,10 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const { return response.error(stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected.")); } - response["result"] = mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format); + // take snapshot on the main coro + doOnIdleOneTime([this, filename, width, height, showui, showhud, rebuild, type, format]() + { mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format); }); + } void LLViewerWindowListener::requestReshape(LLSD const & event_data) const diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index 34f3fb75eb..86aa4095e0 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -141,7 +141,7 @@ end function UI.snapshot(...) local args = mapargs('filename,width,height,showui,showhud,rebuild,type', ...) args.op = 'saveSnapshot' - return leap.request('LLViewerWindow', args).result + return leap.request('LLViewerWindow', args) end -- *************************************************************************** diff --git a/indra/newview/scripts/lua/test_LLChatListener.lua b/indra/newview/scripts/lua/test_LLChatListener.lua index 0f269b54e6..5679e9f98a 100644 --- a/indra/newview/scripts/lua/test_LLChatListener.lua +++ b/indra/newview/scripts/lua/test_LLChatListener.lua @@ -1,5 +1,6 @@ -local LLListener = require 'LLListener' +local LLAgent = require 'LLAgent' local LLChat = require 'LLChat' +local LLListener = require 'LLListener' local UI = require 'UI' -- Chat listener script allows to use the following commands in Nearby chat: @@ -23,9 +24,13 @@ function openOrEcho(message) end local listener = LLListener(LLChat.nearbyChatPump) +local ageint_id = LLAgent.getID() function listener:handleMessages(event_data) - if string.find(event_data.message, '[LUA]') then + -- ignore messages and commands from other avatars + if event_data.from_id ~= ageint_id then + return true + elseif string.find(event_data.message, '[LUA]') then return true elseif event_data.message == 'stop' then LLChat.sendNearby('Closing echo script.') diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index 2158134511..23310c6176 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -14,7 +14,7 @@ function flt:handleEvents(event_data) end function flt:commit_disable_ctrl(event_data) - self:post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)}) + self:post({action="set_enabled", ctrl_name="open_btn", value = not event_data.value}) end function flt:commit_title_cmb(event_data) diff --git a/indra/newview/scripts/lua/test_toolbars.lua b/indra/newview/scripts/lua/test_toolbars.lua index 7683fca8a3..9cd1043446 100644 --- a/indra/newview/scripts/lua/test_toolbars.lua +++ b/indra/newview/scripts/lua/test_toolbars.lua @@ -20,7 +20,7 @@ if response == 'OK' then UI.removeToolbarBtn(BUTTONS[i]) end end - popup:tip('Toolbars were reshuffled') + UI.popup:tip('Toolbars were reshuffled') else - popup:tip('Canceled') + UI.popup:tip('Canceled') end -- cgit v1.2.3 From 4811e4263216a2f282cfe146e247578b50005e42 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Thu, 8 Aug 2024 11:04:35 -0700 Subject: (WIP) Local paintmap modification test --- indra/newview/llterrainpaintmap.cpp | 118 ++++++++++++++++++++- indra/newview/llterrainpaintmap.h | 38 +++++++ indra/newview/llviewermenu.cpp | 46 ++++++++ indra/newview/llvlcomposition.cpp | 8 ++ indra/newview/llvlcomposition.h | 6 ++ indra/newview/skins/default/xui/en/menu_viewer.xml | 7 ++ 6 files changed, 221 insertions(+), 2 deletions(-) diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 8ccde74c93..6979464cd3 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -38,18 +38,31 @@ #include "llsurface.h" #include "llsurfacepatch.h" #include "llviewercamera.h" +#include "llviewercontrol.h" #include "llviewerregion.h" #include "llviewershadermgr.h" #include "llviewertexture.h" -// static -bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) +namespace +{ +#ifdef SHOW_ASSERT +void check_tex(const LLViewerTexture& tex) { llassert(tex.getComponents() == 3); llassert(tex.getWidth() > 0 && tex.getHeight() > 0); llassert(tex.getWidth() == tex.getHeight()); llassert(tex.getPrimaryFormat() == GL_RGB); llassert(tex.getGLTexture()); +} +#endif +} // namespace + +// static +bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) +{ +#ifdef SHOW_ASSERT + check_tex(tex); +#endif const LLSurface& surface = region.getLand(); const U32 patch_count = surface.getPatchesPerEdge(); @@ -283,3 +296,104 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& return success; } + +// TODO: Decide when to apply the paint queue - ideally once per frame per region +// Applies paints and then clears the paint queue +// *NOTE The paint queue is also cleared when setting the paintmap texture +void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueue& queue) +{ + if (queue.empty()) { return; } + +#ifdef SHOW_ASSERT + check_tex(tex); +#endif + + gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true); + + const std::vector& queue_list = queue.get(); + for (size_t i = 0; i < queue_list.size(); ++i) + { + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + queue.convertBitDepths(i, 8); + const LLTerrainPaint::ptr_t& paint = queue_list[i]; + + if (paint->mData.empty()) { continue; } + constexpr GLint level = 0; + if ((paint->mStartX >= tex.getWidth() - 1) || (paint->mStartY >= tex.getHeight() - 1)) { continue; } + constexpr GLint miplevel = 0; + const S32 x_offset = paint->mStartX; + const S32 y_offset = paint->mStartY; + const S32 width = llmin(paint->mWidthX, tex.getWidth() - x_offset); + const S32 height = llmin(paint->mWidthY, tex.getHeight() - y_offset); + const U8* pixels = paint->mData.data(); + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + glTexSubImage2D(GL_TEXTURE_2D, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, pixels); + stop_glerror(); + } + + // Generating mipmaps at the end... + glGenerateMipmap(GL_TEXTURE_2D); + stop_glerror(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + queue.clear(); +} + +bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) +{ + llassert(paint); + if (!paint) { return false; } + + // The paint struct should be pre-validated before this code is reached. + llassert(!paint->mData.empty()); + // The internal paint map image is currently 8 bits, so that's the maximum + // allowed bit depth. + llassert(paint->mBitDepth > 0 && paint->mBitDepth <= 8); + llassert(paint->mData.size() == (LLTerrainPaint::COMPONENTS * paint->mWidthX * paint->mWidthY)); + llassert(paint->mWidthX > 0); + llassert(paint->mWidthY > 0); +#ifdef SHOW_ASSERT + static LLCachedControl max_texture_width(gSavedSettings, "RenderMaxTextureResolution", 2048); +#endif + llassert(paint->mWidthX <= max_texture_width); + llassert(paint->mWidthY <= max_texture_width); + llassert(paint->mStartX < max_texture_width); + llassert(paint->mStartY < max_texture_width); + + mList.push_back(paint); + return true; +} + +bool LLTerrainPaintQueue::empty() const +{ + return mList.empty(); +} + +void LLTerrainPaintQueue::clear() +{ + mList.clear(); +} + +void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) +{ + llassert(target_bit_depth > 0 && target_bit_depth <= 8); + llassert(index < mList.size()); + + LLTerrainPaint::ptr_t& paint = mList[index]; + if (paint->mBitDepth == target_bit_depth) { return; } + + const F32 old_bit_max = F32((1 << paint->mBitDepth) - 1); + const F32 new_bit_max = F32((1 << target_bit_depth) - 1); + const F32 bit_conversion_factor = new_bit_max / old_bit_max; + + for (U8& color : paint->mData) + { + color = (U8)llround(F32(color) * bit_conversion_factor); + } + + paint->mBitDepth = target_bit_depth; +} diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index 66827862c5..9189f12cbd 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -28,6 +28,7 @@ class LLViewerRegion; class LLViewerTexture; +class LLTerrainPaintQueue; class LLTerrainPaintMap { @@ -39,4 +40,41 @@ public: // to type TERRAIN_PAINT_TYPE_PBR_PAINTMAP. // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); + + static void applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueue& queue); +}; + +// Enqueued paint operations, in texture coordinates. +// data is always RGB, with each U8 storing one color in the provided bit depth. +class LLTerrainPaint +{ +public: + using ptr_t = std::shared_ptr; + + U16 mStartX; + U16 mStartY; + U16 mWidthX; + U16 mWidthY; + U8 mBitDepth; + static const U8 COMPONENTS = 3; + std::vector mData; +}; + +class LLTerrainPaintQueue +{ +public: + bool enqueue(LLTerrainPaint::ptr_t& paint); + bool empty() const; + void clear(); + + const std::vector& get() const { return mList; } + + // Convert mBitDepth for the LLTerrainPaint in the queue at index + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + void convertBitDepths(size_t index, U8 target_bit_depth); + +private: + std::vector mList; }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 10506e0e74..fa6e8870b3 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -117,6 +117,7 @@ #include "lltoolmgr.h" #include "lltoolpie.h" #include "lltoolselectland.h" +#include "llterrainpaintmap.h" #include "lltrans.h" #include "llviewerdisplay.h" //for gWindowResized #include "llviewergenericmessage.h" @@ -1448,6 +1449,50 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t } }; +class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerTexture* tex = gLocalTerrainMaterials.getPaintMap(); + if (!tex) + { + LL_WARNS() << "No local paint map available to edit" << LL_ENDL; + return false; + } + + LLTerrainPaintQueue& paint_queue = gLocalTerrainMaterials.getPaintQueue(); + + // Enqueue a paint + // Overrides an entire region patch with the material in the last slot + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + LLTerrainPaint::ptr_t paint = std::make_shared(); + const U16 width = U16(tex->getWidth() / 16); + paint->mStartX = width - 1; + paint->mStartY = width - 1; + paint->mWidthX = width; + paint->mWidthY = width; + constexpr U8 bit_depth = 5; + paint->mBitDepth = bit_depth; + constexpr U8 max_value = (1 << bit_depth) - 1; + const size_t pixel_count = width * width; + paint->mData.resize(LLTerrainPaint::COMPONENTS * pixel_count); + for (size_t pixel = 0; pixel < pixel_count; ++pixel) + { + paint->mData[(LLTerrainPaint::COMPONENTS*pixel) + LLTerrainPaint::COMPONENTS - 1] = max_value; + } + paint_queue.enqueue(paint); + + // Apply the paint queue ad-hoc right here for now. + // *TODO: Eventually the paint queue should be applied at a predictable + // time in the viewer frame loop. + LLTerrainPaintMap::applyPaintQueue(*tex, paint_queue); + + return true; + } +}; + class LLAdvancedTerrainDeleteLocalPaintMap : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -9813,6 +9858,7 @@ void initialize_menus() // Develop > Terrain view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain"); view_listener_t::addMenu(new LLAdvancedTerrainCreateLocalPaintMap(), "Advanced.TerrainCreateLocalPaintMap"); + view_listener_t::addMenu(new LLAdvancedTerrainEditLocalPaintMap(), "Advanced.TerrainEditLocalPaintMap"); view_listener_t::addMenu(new LLAdvancedTerrainDeleteLocalPaintMap(), "Advanced.TerrainDeleteLocalPaintMap"); // Advanced > UI diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index 077e6e6cb1..d87658ba89 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -317,10 +317,18 @@ LLViewerTexture* LLTerrainMaterials::getPaintMap() return mPaintMap.get(); } +LLTerrainPaintQueue& LLTerrainMaterials::getPaintQueue() +{ + return mPaintQueue; +} + void LLTerrainMaterials::setPaintMap(LLViewerTexture* paint_map) { llassert(!paint_map || mPaintType == TERRAIN_PAINT_TYPE_PBR_PAINTMAP); + const bool changed = paint_map != mPaintMap; mPaintMap = paint_map; + // The paint map has changed, so edits are no longer valid + mPaintQueue.clear(); } // Boost the texture loading priority diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index f15f9bff6a..972a46d8db 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -27,10 +27,13 @@ #ifndef LL_LLVLCOMPOSITION_H #define LL_LLVLCOMPOSITION_H +#include + #include "llviewerlayer.h" #include "llviewershadermgr.h" #include "llviewertexture.h" #include "llpointer.h" +#include "llterrainpaintmap.h" #include "llimage.h" @@ -87,6 +90,8 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); + // Paint queue for current paint map + LLTerrainPaintQueue& getPaintQueue(); protected: void unboost(); @@ -105,6 +110,7 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer mPaintMap; + LLTerrainPaintQueue mPaintQueue; }; // Local materials to override all regions diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index e1d33e5bc3..74ced74178 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3824,6 +3824,13 @@ function="World.EnvPreset" + + + Date: Fri, 9 Aug 2024 17:23:43 -0700 Subject: secondlife/viewer#1883: (WIP) Alpha paint queue --- indra/llrender/llglslshader.cpp | 22 ++ indra/llrender/llglslshader.h | 2 + indra/llrender/llshadermgr.cpp | 3 +- indra/llrender/llshadermgr.h | 1 + .../shaders/class1/interface/pbrTerrainBakeF.glsl | 2 +- .../shaders/class1/interface/terrainStampF.glsl | 44 +++ .../shaders/class1/interface/terrainStampV.glsl | 39 +++ indra/newview/lldrawpoolterrain.cpp | 8 +- indra/newview/llterrainpaintmap.cpp | 326 ++++++++++++++++++++- indra/newview/llterrainpaintmap.h | 28 +- indra/newview/llviewermenu.cpp | 33 ++- indra/newview/llviewershadermgr.cpp | 19 ++ indra/newview/llviewershadermgr.h | 1 + indra/newview/llvlcomposition.cpp | 8 +- indra/newview/llvlcomposition.h | 13 +- 15 files changed, 510 insertions(+), 39 deletions(-) create mode 100644 indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl create mode 100644 indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 6ba5463acd..bbbce1965e 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -1138,6 +1138,28 @@ S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextu return uniform; } +// For LLImageGL-wrapped textures created via GL elsewhere with our API only. Use with caution. +S32 LLGLSLShader::bindTextureImageGL(S32 uniform, LLImageGL* texture, LLTexUnit::eTextureType mode) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; + + if (uniform < 0 || uniform >= (S32)mTexture.size()) + { + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << uniform << LL_ENDL; + llassert(false); + return -1; + } + + uniform = mTexture[uniform]; + + if (uniform > -1) + { + gGL.getTexUnit(uniform)->bind(texture); + } + + return uniform; +} + S32 LLGLSLShader::bindTexture(S32 uniform, LLRenderTarget* texture, bool depth, LLTexUnit::eTextureFilterOptions mode, U32 index) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 2d669c70a9..c24daaf686 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -264,6 +264,8 @@ public: // You can reuse the return value to unbind a texture when required. S32 bindTexture(const std::string& uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); S32 bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); + // For LLImageGL-wrapped textures created via GL elsewhere with our API only. Use with caution. + S32 bindTextureImageGL(S32 uniform, LLImageGL* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); S32 bindTexture(const std::string& uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR); S32 bindTexture(S32 uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR, U32 index = 0); S32 unbindTexture(const std::string& uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 6097b09d96..796805e2a5 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1190,8 +1190,9 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("gltf_material_id"); // (GLTF) mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF) + mReservedUniforms.push_back("terrain_stamp_scale"); - llassert(mReservedUniforms.size() == LLShaderMgr::TERRAIN_TEXTURE_TRANSFORMS +1); + llassert(mReservedUniforms.size() == LLShaderMgr::TERRAIN_STAMP_SCALE +1); mReservedUniforms.push_back("viewport"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 1bae0cd8a0..ff07ce454b 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -67,6 +67,7 @@ public: GLTF_MATERIAL_ID, // "gltf_material_id" (GLTF) TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF) + TERRAIN_STAMP_SCALE, // "terrain_stamp_scale" VIEWPORT, // "viewport" LIGHT_POSITION, // "light_position" diff --git a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl index cf20653a0f..a79a56d725 100644 --- a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl +++ b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl @@ -1,5 +1,5 @@ /** - * @file terrainBakeF.glsl + * @file pbrTerrainBakeF.glsl * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code diff --git a/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl new file mode 100644 index 0000000000..e79e9010e6 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl @@ -0,0 +1,44 @@ +/** + * @file terrainStampF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +// Paint texture to stamp into the "paintmap" +uniform sampler2D diffuseMap; + +in vec2 vary_texcoord0; + +void main() +{ + vec4 col = texture(diffuseMap, vary_texcoord0); + if (col.a <= 0.0f) + { + discard; + } + + frag_color = max(col, vec4(0)); +} diff --git a/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl new file mode 100644 index 0000000000..294fa6be26 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl @@ -0,0 +1,39 @@ +/** + * @file terrainStampV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +uniform mat4 modelview_projection_matrix; +uniform vec2 terrain_stamp_scale; + +in vec3 position; + +out vec2 vary_texcoord0; + +void main() +{ + gl_Position = modelview_projection_matrix * vec4(position, 1.0); + // Positions without transforms are treated as UVs for the purpose of this shader. + vary_texcoord0.xy = terrain_stamp_scale * position.xy; +} + diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 5e676bc5b3..57def49539 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -296,7 +296,7 @@ void LLDrawPoolTerrain::renderFullShaderTextures() // GL_BLEND disabled by default drawLoop(); - // Disable multitexture + // Disable textures sShader->disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0); sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL1); @@ -557,7 +557,7 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool use_local_materials) // GL_BLEND disabled by default drawLoop(); - // Disable multitexture + // Disable textures if (paint_type == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE) { @@ -769,7 +769,7 @@ void LLDrawPoolTerrain::renderFull4TU() } LLVertexBuffer::unbind(); - // Disable multitexture + // Disable textures gGL.getTexUnit(3)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(3)->disable(); gGL.getTexUnit(3)->activate(); @@ -949,7 +949,7 @@ void LLDrawPoolTerrain::renderFull2TU() // Restore blend state gGL.setSceneBlendType(LLRender::BT_ALPHA); - // Disable multitexture + // Disable textures gGL.getTexUnit(1)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(1)->disable(); diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 6979464cd3..7dc09a4748 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -31,6 +31,8 @@ // library includes #include "llglslshader.h" #include "llrendertarget.h" +#include "llrender2dutils.h" +#include "llshadermgr.h" #include "llvertexbuffer.h" // newview includes @@ -89,8 +91,8 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& // Bind the debug shader and render terrain to tex // Use a scratch render target because its dimensions may exceed the standard bake target, and this is a one-off bake LLRenderTarget scratch_target; - const S32 dim = llmin(tex.getWidth(), tex.getHeight()); - scratch_target.allocate(dim, dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, LLTexUnit::eTextureMipGeneration::TMG_NONE); if (!scratch_target.isComplete()) { @@ -117,6 +119,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& const F32 region_half_width = region_width / 2.0f; const F32 region_camera_height = surface.getMaxZ() + DEFAULT_NEAR_PLANE; LLViewerCamera camera; + // TODO: Huh... I just realized this view vector is not completely vertical const LLVector3 region_center = LLVector3(region_half_width, region_half_width, 0.0) + region.getOriginAgent(); const LLVector3 camera_origin = LLVector3(0.0f, 0.0f, region_camera_height) + region_center; camera.lookAt(camera_origin, region_center, LLVector3::y_axis); @@ -250,6 +253,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& S32 alpha_ramp = shader.enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); LLPointer alpha_ramp_texture = LLViewerTextureManager::getFetchedTexture(IMG_ALPHA_GRAD_2D); + // TODO: Consider using LLGLSLShader::bindTexture gGL.getTexUnit(alpha_ramp)->bind(alpha_ramp_texture); gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); @@ -263,6 +267,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& const U32 vertex_offset = n * patch_index; llassert(index_offset + ni <= region_indices); llassert(vertex_offset + n <= region_vertices); + // TODO: Try a single big drawRange and see if that still works buf->drawRange(LLRender::TRIANGLES, vertex_offset, vertex_offset + n - 1, ni, index_offset); } } @@ -282,7 +287,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& gGL.flush(); LLVertexBuffer::unbind(); // Final step: Copy the output to the terrain paintmap - const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, dim, dim); + const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, tex.getWidth(), tex.getHeight()); if (!success) { LL_WARNS() << "Failed to copy framebuffer to paintmap" << LL_ENDL; @@ -297,10 +302,10 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& return success; } -// TODO: Decide when to apply the paint queue - ideally once per frame per region +// *TODO: Decide when to apply the paint queue - ideally once per frame per region // Applies paints and then clears the paint queue // *NOTE The paint queue is also cleared when setting the paintmap texture -void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueue& queue) +void LLTerrainPaintMap::applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue) { if (queue.empty()) { return; } @@ -310,6 +315,11 @@ void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueu gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true); + // glTexSubImage2D replaces all pixels in the rectangular region. That + // makes it unsuitable for alpha. + llassert(queue.getComponents() == LLTerrainPaint::RGB); + constexpr GLenum pixformat = GL_RGB; + const std::vector& queue_list = queue.get(); for (size_t i = 0; i < queue_list.size(); ++i) { @@ -328,8 +338,10 @@ void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueu const S32 width = llmin(paint->mWidthX, tex.getWidth() - x_offset); const S32 height = llmin(paint->mWidthY, tex.getHeight() - y_offset); const U8* pixels = paint->mData.data(); - constexpr GLenum pixformat = GL_RGB; constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + // *TODO: Performance suggestion: Use the sub-image utility function + // that LLImageGL::setSubImage uses to split texture updates into + // lines, if that's faster. glTexSubImage2D(GL_TEXTURE_2D, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, pixels); stop_glerror(); } @@ -343,7 +355,284 @@ void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueu queue.clear(); } -bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) +namespace +{ + +// A general-purpose vertex buffer of a quad for stamping textures on the z=0 +// plane. +// *NOTE: Because we know the vertex XY coordinates go from 0 to 1 +// pre-transform, UVs can be calculated from the vertices +LLVertexBuffer& get_paint_triangle_buffer() +{ + static LLPointer buf = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX); + static bool initialized = false; + if (!initialized) + { + // Two triangles forming a square from (0,0) to (1,1) + buf->allocateBuffer(/*vertices =*/ 4, /*indices =*/ 6); + LLStrider indices; + LLStrider vertices; + buf->getVertexStrider(vertices); + buf->getIndexStrider(indices); + // y + // 2....3 + // ^ . . + // | 0....1 + // | + // -------> x + // + // triangle 1: 0,1,2 + // triangle 2: 1,3,2 + (*(vertices++)).set(0.0f, 0.0f, 0.0f); + (*(vertices++)).set(1.0f, 0.0f, 0.0f); + (*(vertices++)).set(0.0f, 1.0f, 0.0f); + (*(vertices++)).set(1.0f, 1.0f, 0.0f); + *(indices++) = 0; + *(indices++) = 1; + *(indices++) = 2; + *(indices++) = 1; + *(indices++) = 3; + *(indices++) = 2; + buf->unmapBuffer(); + } + return *buf; +} + +}; + +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in) +{ +#ifdef SHOW_ASSERT + check_tex(tex); +#endif + llassert(queue_in.getComponents() == LLTerrainPaint::RGBA); + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the paints, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = &get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glh::matrix4f(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glh::matrix4f model; + { + model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each RGBA paint to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector& queue_in_list = queue_in.get(); + for (size_t i = 0; i < queue_in_list.size(); ++i) + { + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for paint operations (this + // could change in the future). + queue_in.convertBitDepths(i, 8); + const LLTerrainPaint::ptr_t& paint_in = queue_in_list[i]; + + // Modelview matrix for the current paint + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + glh::matrix4f model; + { + model.set_scale(glh::vec3f(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); + model.set_translate(glh::vec3f(paint_in->mStartX, paint_in->mStartY, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + // Generate temporary stamp texture from paint contents. + // Our stamp image needs to be a power of two. + // Because the paint data may not cover a whole power-of-two region, + // allocate a bigger 2x2 image if needed, but set the image data later + // for a subset of the image. + // Pixel data outside this subset is left undefined. We will use + // TERRAIN_STAMP_SCALE in the stamp shader to define the subset of the + // image we care about. + const U32 width_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthX)))); + const U32 height_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthY)))); + LLPointer stamp_image; + { + // Create image object (dimensions not yet initialized in GL) + U32 stamp_tex_name; + LLImageGL::generateTextures(1, &stamp_tex_name); + const U32 components = paint_in->mComponents; + constexpr LLGLenum target = GL_TEXTURE_2D; + const LLGLenum internal_format = paint_in->mComponents == 4 ? GL_RGBA8 : GL_RGB8; + const LLGLenum format = paint_in->mComponents == 4 ? GL_RGBA : GL_RGB; + constexpr LLGLenum type = GL_UNSIGNED_BYTE; + stamp_image = new LLImageGL(stamp_tex_name, components, target, internal_format, format, type, LLTexUnit::TAM_WRAP); + // Nearest-neighbor filtering to reduce surprises + stamp_image->setFilteringOption(LLTexUnit::TFO_POINT); + + // Initialize the image dimensions in GL + constexpr U8* undefined_data_for_now = nullptr; + gGL.getTexUnit(0)->bind(stamp_image, false, true); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_rounded, height_rounded, 0, format, type, undefined_data_for_now); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + stamp_image->setSize(width_rounded, height_rounded, components); + stamp_image->setDiscardLevel(0); + + // Manually set a subset of the image in GL + const U8* data = paint_in->mData.data(); + const S32 data_width = paint_in->mWidthX; + const S32 data_height = paint_in->mWidthY; + constexpr S32 origin = 0; + // width = data_width; height = data_height. i.e.: Copy the full + // contents of data into the image. + stamp_image->setSubImage(data, data_width, data_height, origin, origin, /*width=*/data_width, /*height=*/data_height); + } + + // Apply ("stamp") the paint to the render target + { + shader.bindTextureImageGL(LLShaderMgr::DIFFUSE_MAP, stamp_image); + const F32 width_fraction = F32(paint_in->mWidthX) / F32(width_rounded); + const F32 height_fraction = F32(paint_in->mWidthY) / F32(height_rounded); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, width_fraction, height_fraction); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared(); + { + paint_out->mStartX = paint_in->mStartX; + paint_out->mStartY = paint_in->mStartY; + paint_out->mWidthX = paint_in->mWidthX; + paint_out->mWidthY = paint_in->mWidthY; + paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mComponents = LLTerrainPaint::RGB; + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = llmin(paint_out->mWidthX, tex.getWidth() - x_offset); + const S32 height = llmin(paint_out->mWidthY, tex.getHeight() - y_offset); + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + queue_out.convertBitDepths(queue_out.size()-1, 5); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(U8 components) +: mComponents(components) +{ + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(const LLTerrainPaintQueue& other) +{ + *this = other; + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(const LLTerrainPaintQueue& other) +{ + mComponents = other.mComponents; + mList = other.mList; + return *this; +} + +bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run) { llassert(paint); if (!paint) { return false; } @@ -353,7 +642,7 @@ bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) // The internal paint map image is currently 8 bits, so that's the maximum // allowed bit depth. llassert(paint->mBitDepth > 0 && paint->mBitDepth <= 8); - llassert(paint->mData.size() == (LLTerrainPaint::COMPONENTS * paint->mWidthX * paint->mWidthY)); + llassert(paint->mData.size() == (mComponents * paint->mWidthX * paint->mWidthY)); llassert(paint->mWidthX > 0); llassert(paint->mWidthY > 0); #ifdef SHOW_ASSERT @@ -364,10 +653,29 @@ bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) llassert(paint->mStartX < max_texture_width); llassert(paint->mStartY < max_texture_width); - mList.push_back(paint); + if (!dry_run) { mList.push_back(paint); } return true; } +bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& paint_queue) +{ + constexpr bool dry_run = true; + for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) + { + if (!enqueue(paint), dry_run) { return false; } + } + for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) + { + enqueue(paint); + } + return true; +} + +size_t LLTerrainPaintQueue::size() const +{ + return mList.size(); +} + bool LLTerrainPaintQueue::empty() const { return mList.empty(); diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index 9189f12cbd..b4d706b107 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -26,6 +26,8 @@ #pragma once +#include "llviewerprecompiledheaders.h" + class LLViewerRegion; class LLViewerTexture; class LLTerrainPaintQueue; @@ -41,14 +43,15 @@ public: // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); - static void applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + static void applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + static LLTerrainPaintQueue convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in); }; // Enqueued paint operations, in texture coordinates. -// data is always RGB, with each U8 storing one color in the provided bit depth. -class LLTerrainPaint +// mData is always RGB or RGBA (determined by mComponents), with each U8 +// storing one color with a max value of (1 >> mBitDepth) - 1 +struct LLTerrainPaint { -public: using ptr_t = std::shared_ptr; U16 mStartX; @@ -56,25 +59,38 @@ public: U16 mWidthX; U16 mWidthY; U8 mBitDepth; - static const U8 COMPONENTS = 3; + U8 mComponents; + const static U8 RGB = 3; + const static U8 RGBA = 4; std::vector mData; }; class LLTerrainPaintQueue { public: - bool enqueue(LLTerrainPaint::ptr_t& paint); + // components determines what type of LLTerrainPaint is allowed. Must be 3 (RGB) or 4 (RGBA) + LLTerrainPaintQueue(U8 components); + LLTerrainPaintQueue(const LLTerrainPaintQueue& other); + LLTerrainPaintQueue& operator=(const LLTerrainPaintQueue& other); + + bool enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run = false); + bool enqueue(LLTerrainPaintQueue& paint_queue); + size_t size() const; bool empty() const; void clear(); const std::vector& get() const { return mList; } + U8 getComponents() const { return mComponents; } // Convert mBitDepth for the LLTerrainPaint in the queue at index + // If mBitDepth is already equal to target_bit_depth, no conversion takes + // place. // It is currently the responsibility of the paint queue to convert // incoming bits to the right bit depth for the paintmap (this could // change in the future). void convertBitDepths(size_t index, U8 target_bit_depth); private: + U8 mComponents; std::vector mList; }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index fa6e8870b3..c4bb5eaa2e 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1435,6 +1435,7 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution"); dim = llclamp(dim, 16, max_resolution); dim = 1 << U32(std::ceil(std::log2(dim))); + // TODO: Could probably get away with not using image raw here, now that we aren't writing bits via the CPU (see load_exr for example) LLPointer image_raw = new LLImageRaw(dim,dim,3); LLPointer tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true); const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex); @@ -1460,7 +1461,7 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t return false; } - LLTerrainPaintQueue& paint_queue = gLocalTerrainMaterials.getPaintQueue(); + LLTerrainPaintQueue& paint_request_queue = gLocalTerrainMaterials.getPaintRequestQueue(); // Enqueue a paint // Overrides an entire region patch with the material in the last slot @@ -1477,17 +1478,31 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t paint->mBitDepth = bit_depth; constexpr U8 max_value = (1 << bit_depth) - 1; const size_t pixel_count = width * width; - paint->mData.resize(LLTerrainPaint::COMPONENTS * pixel_count); - for (size_t pixel = 0; pixel < pixel_count; ++pixel) + const U8 components = LLTerrainPaint::RGBA; + paint->mComponents = components; + paint->mData.resize(components * pixel_count); + for (size_t h = 0; h < paint->mWidthY; ++h) { - paint->mData[(LLTerrainPaint::COMPONENTS*pixel) + LLTerrainPaint::COMPONENTS - 1] = max_value; + for (size_t w = 0; w < paint->mWidthX; ++w) + { + const size_t pixel = (h * paint->mWidthX) + w; + // Solid blue color + paint->mData[(components*pixel) + components - 2] = max_value; // blue + // Alpha gradient from 0.0 to 1.0 along w + const U8 alpha = U8(F32(max_value) * F32(w+1) / F32(paint->mWidthX)); + paint->mData[(components*pixel) + components - 1] = alpha; // alpha + } } - paint_queue.enqueue(paint); + paint_request_queue.enqueue(paint); - // Apply the paint queue ad-hoc right here for now. - // *TODO: Eventually the paint queue should be applied at a predictable - // time in the viewer frame loop. - LLTerrainPaintMap::applyPaintQueue(*tex, paint_queue); + // Apply the paint queues ad-hoc right here for now. + // *TODO: Eventually the paint queue(s) should be applied at a + // predictable time in the viewer frame loop. + // TODO: In hindsight... maybe we *should* bind the paintmap to the render buffer. That makes a lot more sense, and we wouldn't have to reduce its resolution by settling for the bake buffer. If we do that, make a comment above convertPaintQueueRGBAToRGB that the texture is modified! + LLTerrainPaintQueue paint_send_queue = LLTerrainPaintMap::convertPaintQueueRGBAToRGB(*tex, paint_request_queue); + LLTerrainPaintQueue& paint_map_queue = gLocalTerrainMaterials.getPaintMapQueue(); + paint_map_queue.enqueue(paint_send_queue); + LLTerrainPaintMap::applyPaintQueueRGB(*tex, paint_map_queue); return true; } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index a8fe221d98..8e7af28d41 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -100,6 +100,7 @@ LLGLSLShader gReflectionProbeDisplayProgram; LLGLSLShader gCopyProgram; LLGLSLShader gCopyDepthProgram; LLGLSLShader gPBRTerrainBakeProgram; +LLGLSLShader gTerrainStampProgram; //object shaders LLGLSLShader gObjectPreviewProgram; @@ -3170,6 +3171,24 @@ bool LLViewerShaderMgr::loadShadersInterface() } } + if (success) + { + LLGLSLShader* shader = &gTerrainStampProgram; + U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); + // LLTerrainPaintMap currently uses an RGB8 texture internally + bit_depth = llclamp(bit_depth, 1, 8); + shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); + + shader->mShaderFiles.clear(); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); + shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + const U32 value_range = (1 << bit_depth) - 1; + shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); + success = success && shader->createShader(); + llassert(success); + } + if (success) { gAlphaMaskProgram.mName = "Alpha Mask Shader"; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index b08796025a..e654967c46 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -175,6 +175,7 @@ extern LLGLSLShader gReflectionProbeDisplayProgram; extern LLGLSLShader gCopyProgram; extern LLGLSLShader gCopyDepthProgram; extern LLGLSLShader gPBRTerrainBakeProgram; +extern LLGLSLShader gTerrainStampProgram; //output tex0[tc0] - tex1[tc1] extern LLGLSLShader gTwoTextureCompareProgram; diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index d87658ba89..ca76d93cd7 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -317,18 +317,14 @@ LLViewerTexture* LLTerrainMaterials::getPaintMap() return mPaintMap.get(); } -LLTerrainPaintQueue& LLTerrainMaterials::getPaintQueue() -{ - return mPaintQueue; -} - void LLTerrainMaterials::setPaintMap(LLViewerTexture* paint_map) { llassert(!paint_map || mPaintType == TERRAIN_PAINT_TYPE_PBR_PAINTMAP); const bool changed = paint_map != mPaintMap; mPaintMap = paint_map; // The paint map has changed, so edits are no longer valid - mPaintQueue.clear(); + mPaintRequestQueue.clear(); + mPaintMapQueue.clear(); } // Boost the texture loading priority diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 972a46d8db..3f1124a8ac 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -90,8 +90,14 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); - // Paint queue for current paint map - LLTerrainPaintQueue& getPaintQueue(); + // Queue of client-triggered paint operations that need to be converted + // into a form that can be sent to the server. + // Paints in this queue are in RGBA format. + LLTerrainPaintQueue& getPaintRequestQueue() { return mPaintRequestQueue; } + // Paint queue for current paint map - this queue gets applied directly to + // the paint map. Paints within are assumed to have already been sent to + // the server. Paints in this queue are in RGB format. + LLTerrainPaintQueue& getPaintMapQueue() { return mPaintMapQueue; } protected: void unboost(); @@ -110,7 +116,8 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer mPaintMap; - LLTerrainPaintQueue mPaintQueue; + LLTerrainPaintQueue mPaintRequestQueue{U8(4)}; + LLTerrainPaintQueue mPaintMapQueue{U8(3)}; }; // Local materials to override all regions -- cgit v1.2.3 From 642ace3c75d08cc55370374f5a091f0c8ff41a4c Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Fri, 16 Aug 2024 17:27:57 -0700 Subject: secondlife/viewer#1883: (WIP) (not yet working) Brush queue --- indra/newview/llterrainpaintmap.cpp | 349 ++++++++++++++++++++++++++++++++---- indra/newview/llterrainpaintmap.h | 90 ++++++++-- indra/newview/llviewermenu.cpp | 40 ++++- indra/newview/llvlcomposition.h | 5 + 4 files changed, 434 insertions(+), 50 deletions(-) diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 7dc09a4748..6a57605325 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -575,12 +575,16 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur paint_out->mWidthY = paint_in->mWidthY; paint_out->mBitDepth = 8; // Will be reduced to 5 bits later paint_out->mComponents = LLTerrainPaint::RGB; +#ifdef SHOW_ASSERT + paint_out->assert_confined_to(tex); +#endif + paint_out->confine_to(tex); paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); constexpr GLint miplevel = 0; const S32 x_offset = paint_out->mStartX; const S32 y_offset = paint_out->mStartY; - const S32 width = llmin(paint_out->mWidthX, tex.getWidth() - x_offset); - const S32 height = llmin(paint_out->mWidthY, tex.getHeight() - y_offset); + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; constexpr GLenum pixformat = GL_RGB; constexpr GLenum pixtype = GL_UNSIGNED_BYTE; llassert(paint_out->mData.size() <= std::numeric_limits::max()); @@ -613,22 +617,294 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur return queue_out; } +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in) +{ +#ifdef SHOW_ASSERT + check_tex(tex); +#endif + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the brushes, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = &get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glh::matrix4f(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glh::matrix4f model; + { + model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each brush stroke to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector& brush_list = queue_in.get(); + for (size_t i = 0; i < brush_list.size(); ++i) + { + const LLTerrainBrush::ptr_t& brush_in = brush_list[i]; + + // Modelview matrix for the current brush + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + // *NOTE: Brush path information is in region space. It will need to be + // converted to paintmap pixel space before it makes sense. + F32 brush_width_x; + F32 brush_width_y; + F32 brush_start_x; + F32 brush_start_y; + { + F32 min_x = brush_in->mPath[0].mV[VX]; + F32 max_x = min_x; + F32 min_y = brush_in->mPath[0].mV[VY]; + F32 max_y = min_y; + for (size_t i = 1; i < brush_in->mPath.size(); ++i) + { + const F32 x = brush_in->mPath[i].mV[VX]; + const F32 y = brush_in->mPath[i].mV[VY]; + min_x = llmin(min_x, x); + max_x = llmax(max_x, x); + min_y = llmin(min_y, y); + max_y = llmax(max_y, y); + } + brush_width_x = brush_in->mBrushSize + (max_x - min_x); + brush_width_y = brush_in->mBrushSize + (max_y - min_y); + brush_start_x = min_x - (brush_in->mBrushSize / 2.0f); + brush_start_y = min_y - (brush_in->mBrushSize / 2.0f); + // Convert brush path information to paintmap pixel space from region + // space. + brush_width_x *= tex.getWidth() / region.getWidth(); + brush_width_y *= tex.getHeight() / region.getWidth(); + brush_start_x *= tex.getWidth() / region.getWidth(); + brush_start_y *= tex.getHeight() / region.getWidth(); + } + glh::matrix4f model; + { + model.set_scale(glh::vec3f(brush_width_x, brush_width_y, 1.0f)); + model.set_translate(glh::vec3f(brush_start_x, brush_start_y, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + // Apply the "brush" to the render target + { + // TODO: Use different shader for this - currently this is using the stamp shader. The white image is just a placeholder for now + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, LLViewerFetchedTexture::sWhiteImagep); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared(); + { + paint_out->mStartX = U16(floor(brush_start_x)); + paint_out->mStartY = U16(floor(brush_start_y)); + const F32 dX = brush_start_x - F32(paint_out->mStartX); + const F32 dY = brush_start_y - F32(paint_out->mStartY); + paint_out->mWidthX = U16(ceil(brush_width_x + dX)); + paint_out->mWidthY = U16(ceil(brush_width_y + dY)); + paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mComponents = LLTerrainPaint::RGB; + // The brush strokes are expected to sometimes partially venture + // outside of the paintmap bounds. + paint_out->confine_to(tex); + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + queue_out.convertBitDepths(queue_out.size()-1, 5); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +template +LLTerrainQueue::LLTerrainQueue(LLTerrainQueue& other) +{ + *this = other; +} + +template +LLTerrainQueue& LLTerrainQueue::operator=(LLTerrainQueue& other) +{ + mList = other.mList; + return *this; +} + +template +bool LLTerrainQueue::enqueue(std::shared_ptr& t, bool dry_run) +{ + if (!dry_run) { mList.push_back(t); } + return true; +} + +template +bool LLTerrainQueue::enqueue(std::vector>& list) +{ + constexpr bool dry_run = true; + for (T::ptr_t& t : list) + { + if (!enqueue(t), dry_run) { return false; } + } + for (T::ptr_t& t : list) + { + enqueue(t); + } + return true; +} + +template +size_t LLTerrainQueue::size() const +{ + return mList.size(); +} + +template +bool LLTerrainQueue::empty() const +{ + return mList.empty(); +} + +template +void LLTerrainQueue::clear() +{ + mList.clear(); +} + +void LLTerrainPaint::assert_confined_to(const LLTexture& tex) const +{ + llassert(mStartX >= 0 && mStartX < tex.getWidth()); + llassert(mStartY >= 0 && mStartY < tex.getHeight()); + llassert(mWidthX <= tex.getWidth() - mStartX); + llassert(mWidthY <= tex.getHeight() - mStartY); +} + +void LLTerrainPaint::confine_to(const LLTexture& tex) +{ + mStartX = llmax(mStartX, 0); + mStartY = llmax(mStartY, 0); + mWidthX = llmin(mWidthX, tex.getWidth() - mStartX); + mWidthY = llmin(mWidthY, tex.getHeight() - mStartY); + assert_confined_to(tex); +} + LLTerrainPaintQueue::LLTerrainPaintQueue(U8 components) : mComponents(components) { llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); } -LLTerrainPaintQueue::LLTerrainPaintQueue(const LLTerrainPaintQueue& other) +LLTerrainPaintQueue::LLTerrainPaintQueue(LLTerrainPaintQueue& other) +: LLTerrainQueue(other) +, mComponents(other.mComponents) { - *this = other; llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); } -LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(const LLTerrainPaintQueue& other) +LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(LLTerrainPaintQueue& other) { + LLTerrainQueue::operator=(other); mComponents = other.mComponents; - mList = other.mList; return *this; } @@ -653,37 +929,12 @@ bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run) llassert(paint->mStartX < max_texture_width); llassert(paint->mStartY < max_texture_width); - if (!dry_run) { mList.push_back(paint); } - return true; + return LLTerrainQueue::enqueue(paint, dry_run); } -bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& paint_queue) +bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& queue) { - constexpr bool dry_run = true; - for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) - { - if (!enqueue(paint), dry_run) { return false; } - } - for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) - { - enqueue(paint); - } - return true; -} - -size_t LLTerrainPaintQueue::size() const -{ - return mList.size(); -} - -bool LLTerrainPaintQueue::empty() const -{ - return mList.empty(); -} - -void LLTerrainPaintQueue::clear() -{ - mList.clear(); + return LLTerrainQueue::enqueue(queue.mList); } void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) @@ -705,3 +956,33 @@ void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) paint->mBitDepth = target_bit_depth; } + +LLTerrainBrushQueue::LLTerrainBrushQueue() +: LLTerrainQueue() +{ +} + +LLTerrainBrushQueue::LLTerrainBrushQueue(LLTerrainBrushQueue& other) +: LLTerrainQueue(other) +{ +} + +LLTerrainBrushQueue& LLTerrainBrushQueue::operator=(LLTerrainBrushQueue& other) +{ + LLTerrainQueue::operator=(other); + return *this; +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run) +{ + llassert(brush->mBrushSize > 0); + llassert(!brush->mPath.empty()); + llassert(brush->mPathOffset < brush->mPath.size()); + llassert(brush->mPathOffset < 2); // Harmless, but doesn't do anything useful, so might be a sign of implementation error + return LLTerrainQueue::enqueue(brush, dry_run); +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrushQueue& queue) +{ + return LLTerrainQueue::enqueue(queue.mList); +} diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index b4d706b107..cffdad80a2 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -28,10 +28,14 @@ #include "llviewerprecompiledheaders.h" +class LLTexture; class LLViewerRegion; class LLViewerTexture; class LLTerrainPaintQueue; +class LLTerrainBrushQueue; +// TODO: Terrain painting across regions. Assuming painting is confined to one +// region for now. class LLTerrainPaintMap { public: @@ -43,8 +47,34 @@ public: // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); + // This operation clears the queue + // TODO: Decide if clearing the queue is needed - seems inconsistent static void applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + static LLTerrainPaintQueue convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in); + // TODO: Implement (it's similar to convertPaintQueueRGBAToRGB but different shader + need to calculate the dimensions + need a different vertex buffer for each brush stroke) + static LLTerrainPaintQueue convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in); +}; + +template +class LLTerrainQueue +{ +public: + LLTerrainQueue() = default; + LLTerrainQueue(LLTerrainQueue& other); + LLTerrainQueue& operator=(LLTerrainQueue& other); + + bool enqueue(std::shared_ptr& t, bool dry_run = false); + size_t size() const; + bool empty() const; + void clear(); + + const std::vector>& get() const { return mList; } + +protected: + bool enqueue(std::vector>& list); + + std::vector> mList; }; // Enqueued paint operations, in texture coordinates. @@ -63,23 +93,27 @@ struct LLTerrainPaint const static U8 RGB = 3; const static U8 RGBA = 4; std::vector mData; + + // Asserts that this paint's start/width fit within the bounds of the + // provided texture dimensions. + void assert_confined_to(const LLTexture& tex) const; + // Confines this paint's start/width so it fits within the bounds of the + // provided texture dimensions. + // Does not allocate mData. + void confine_to(const LLTexture& tex); }; -class LLTerrainPaintQueue +class LLTerrainPaintQueue : public LLTerrainQueue { public: + LLTerrainPaintQueue() = delete; // components determines what type of LLTerrainPaint is allowed. Must be 3 (RGB) or 4 (RGBA) LLTerrainPaintQueue(U8 components); - LLTerrainPaintQueue(const LLTerrainPaintQueue& other); - LLTerrainPaintQueue& operator=(const LLTerrainPaintQueue& other); + LLTerrainPaintQueue(LLTerrainPaintQueue& other); + LLTerrainPaintQueue& operator=(LLTerrainPaintQueue& other); bool enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run = false); - bool enqueue(LLTerrainPaintQueue& paint_queue); - size_t size() const; - bool empty() const; - void clear(); - - const std::vector& get() const { return mList; } + bool enqueue(LLTerrainPaintQueue& queue); U8 getComponents() const { return mComponents; } // Convert mBitDepth for the LLTerrainPaint in the queue at index @@ -92,5 +126,41 @@ public: private: U8 mComponents; - std::vector mList; +}; + +struct LLTerrainBrush +{ + using ptr_t = std::shared_ptr; + + // Width of the brush in region space. The brush is a square texture with + // alpha. + F32 mBrushSize; + // Brush path points in region space, excluding the vertical axis, which + // does not contribute to the paint map. + std::vector mPath; + // Offset of the brush path to actually start drawing at. An offset of 0 + // indicates that a brush stroke has just started (i.e. the user just + // pressed down the mouse button). An offset greater than 0 indicates the + // continuation of a brush stroke. Skipped entries in mPath are not drawn + // directly, but are used for stroke orientation and path interpolation. + // TODO: For the initial implementation, mPathOffset will be 0 and mPath + // will be of length of at most 1, leading to discontinuous paint paths. + // Then, mPathOffset may be 1 or 0, 1 indicating the continuation of a + // stroke with linear interpolation. It is unlikely that we will implement + // anything more sophisticated than that for now. + U8 mPathOffset; + // Indicates if this is the end of the brush stroke. Can occur if the mouse + // button is lifted, or if the mouse temporarily stops while held down. + bool mPathEnd; +}; + +class LLTerrainBrushQueue : public LLTerrainQueue +{ +public: + LLTerrainBrushQueue(); + LLTerrainBrushQueue(LLTerrainBrushQueue& other); + LLTerrainBrushQueue& operator=(LLTerrainBrushQueue& other); + + bool enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run = false); + bool enqueue(LLTerrainBrushQueue& queue); }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index c4bb5eaa2e..dd6b6d1ca1 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1461,17 +1461,38 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t return false; } + LLTerrainBrushQueue& brush_queue = gLocalTerrainMaterials.getBrushQueue(); LLTerrainPaintQueue& paint_request_queue = gLocalTerrainMaterials.getPaintRequestQueue(); + const LLViewerRegion* region = gAgent.getRegion(); + if (!region) + { + LL_WARNS() << "No current region for calculating paint operations" << LL_ENDL; + return false; + } + // TODO: Create the brush + // Just a dab for now + LLTerrainBrush::ptr_t brush = std::make_shared(); + brush->mBrushSize = 16.0f; + brush->mPath.emplace_back(17.0f, 17.0f); + brush->mPathOffset = 0; + brush->mPathEnd = true; + brush_queue.enqueue(brush); + LLTerrainPaintQueue brush_as_paint_queue = LLTerrainPaintMap::convertBrushQueueToPaintRGB(*region, *tex, brush_queue); + //paint_send_queue.enqueue(brush_as_paint_queue); // TODO: What was this line for? (it might also be a leftover line from an unfinished edit) + + // TODO: Keep this around for later testing (i.e. when reducing framebuffer size and the offsets that requires) +#if 0 // Enqueue a paint - // Overrides an entire region patch with the material in the last slot + // Modifies a subsection of the region paintmap with the material in + // the last slot. // It is currently the responsibility of the paint queue to convert // incoming bits to the right bit depth for the paintmap (this could // change in the future). LLTerrainPaint::ptr_t paint = std::make_shared(); - const U16 width = U16(tex->getWidth() / 16); - paint->mStartX = width - 1; - paint->mStartY = width - 1; + const U16 width = 33; + paint->mStartX = 1; + paint->mStartY = 1; paint->mWidthX = width; paint->mWidthY = width; constexpr U8 bit_depth = 5; @@ -1488,12 +1509,19 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t const size_t pixel = (h * paint->mWidthX) + w; // Solid blue color paint->mData[(components*pixel) + components - 2] = max_value; // blue - // Alpha gradient from 0.0 to 1.0 along w - const U8 alpha = U8(F32(max_value) * F32(w+1) / F32(paint->mWidthX)); + //// Alpha grid: 1.0 if odd for either dimension, 0.0 otherwise + //const U8 alpha = U8(F32(max_value) * F32(bool(w % 2) || bool(h % 2))); + //paint->mData[(components*pixel) + components - 1] = alpha; // alpha + // Alpha "frame" + const bool edge = w == 0 || h == 0 || w == (paint->mWidthX - 1) || h == (paint->mWidthY - 1); + const bool near_edge_frill = ((w == 1 || w == (paint->mWidthX - 2)) && (h % 2 == 0)) || + ((h == 1 || h == (paint->mWidthY - 2)) && (w % 2 == 0)); + const U8 alpha = U8(F32(max_value) * F32(edge || near_edge_frill)); paint->mData[(components*pixel) + components - 1] = alpha; // alpha } } paint_request_queue.enqueue(paint); +#endif // Apply the paint queues ad-hoc right here for now. // *TODO: Eventually the paint queue(s) should be applied at a diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 3f1124a8ac..9a5d74adda 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -90,6 +90,10 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); + // Queue of client-triggered brush operations that need to be converted + // into a form that can be sent to the server. + // TODO: Consider getting rid of mPaintRequestQueue, as it's not really needed (brushes go directly to RGB queue) + LLTerrainBrushQueue& getBrushQueue() { return mBrushQueue; } // Queue of client-triggered paint operations that need to be converted // into a form that can be sent to the server. // Paints in this queue are in RGBA format. @@ -116,6 +120,7 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer mPaintMap; + LLTerrainBrushQueue mBrushQueue; LLTerrainPaintQueue mPaintRequestQueue{U8(4)}; LLTerrainPaintQueue mPaintMapQueue{U8(3)}; }; -- cgit v1.2.3 From 81cc4fa7d61e838413b912a4ed2d957cc65bcb46 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 7 Oct 2024 14:30:29 -0700 Subject: secondlife/viewer#1883: (WIP/Alpha/WOMM) Fix some compiler and runtime errors --- indra/newview/llterrainpaintmap.cpp | 52 ++++++++++++++++++------------------- indra/newview/llviewercontrol.cpp | 1 + indra/newview/llviewermenu.cpp | 8 ++++-- indra/newview/llviewershadermgr.cpp | 38 +++++++++++++++++---------- 4 files changed, 57 insertions(+), 42 deletions(-) diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 6a57605325..d36e4ea657 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -447,7 +447,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // View matrix // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. // Camera is centered in the middle of the framebuffer. - glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + glm::mat4 view(glm::make_mat4((GLfloat*) OGL_TO_CFR_ROTATION)); { LLViewerCamera camera; const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); @@ -456,7 +456,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); GLfloat ogl_matrix[16]; camera.getOpenGLTransform(ogl_matrix); - view *= glh::matrix4f(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); } LLGLDisable stencil(GL_STENCIL_TEST); @@ -471,14 +471,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // First, apply the paint map as the background { - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); - model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); // We care about the whole paintmap, which is already a power of two. @@ -505,14 +505,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // Modelview matrix for the current paint // View matrix is already computed. Just need the model matrix. // Orthographic projection matrix is already updated - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); - model.set_translate(glh::vec3f(paint_in->mStartX, paint_in->mStartY, 0.0f)); + model = glm::scale(model, glm::vec3(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); + model = glm::translate(model, glm::vec3(paint_in->mStartX, paint_in->mStartY, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); // Generate temporary stamp texture from paint contents. // Our stamp image needs to be a power of two. @@ -663,7 +663,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe // View matrix // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. // Camera is centered in the middle of the framebuffer. - glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + glm::mat4 view(glm::make_mat4((GLfloat*)OGL_TO_CFR_ROTATION)); { LLViewerCamera camera; const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); @@ -672,7 +672,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); GLfloat ogl_matrix[16]; camera.getOpenGLTransform(ogl_matrix); - view *= glh::matrix4f(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); } LLGLDisable stencil(GL_STENCIL_TEST); @@ -687,14 +687,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe // First, apply the paint map as the background { - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); - model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); // We care about the whole paintmap, which is already a power of two. @@ -748,14 +748,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe brush_start_x *= tex.getWidth() / region.getWidth(); brush_start_y *= tex.getHeight() / region.getWidth(); } - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f(brush_width_x, brush_width_y, 1.0f)); - model.set_translate(glh::vec3f(brush_start_x, brush_start_y, 0.0f)); + model = glm::scale(model, glm::vec3(brush_width_x, brush_width_y, 1.0f)); + model = glm::translate(model, glm::vec3(brush_start_x, brush_start_y, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); // Apply the "brush" to the render target { @@ -842,11 +842,11 @@ template bool LLTerrainQueue::enqueue(std::vector>& list) { constexpr bool dry_run = true; - for (T::ptr_t& t : list) + for (auto& t : list) { if (!enqueue(t), dry_run) { return false; } } - for (T::ptr_t& t : list) + for (auto& t : list) { enqueue(t); } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index f8a315d4d8..184c0e7d8b 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -911,6 +911,7 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged); setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged); { + setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleLocalTerrainChanged); const char* transform_suffixes[] = { "ScaleU", diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index dd6b6d1ca1..b39bf4fdbd 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1430,6 +1430,12 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t return false; } + // This calls gLocalTerrainMaterials.setPaintType + // It also ensures the terrain bake shader is compiled (handleSetShaderChanged), so call this first + // *TODO: Fix compile errors in shader so it can be used for all platforms. Then we can unhide the shader from behind this setting and remove the hook to handleSetShaderChanged. This advanced setting is intended to be used as a local setting for testing terrain, not a feature flag, but it is currently used like a feature flag as a temporary hack. + // *TODO: Ideally we would call setPaintType *after* the paint map is well-defined. The terrain draw pool should be able to handle an undefined paint map in the meantime. + gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); + U16 dim = (U16)gSavedSettings.getU32("TerrainPaintResolution"); // Ensure a reasonable image size of power two const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution"); @@ -1439,8 +1445,6 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t LLPointer image_raw = new LLImageRaw(dim,dim,3); LLPointer tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true); const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex); - // This calls gLocalTerrainMaterials.setPaintType - gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); // If baking the paintmap failed, set the paintmap to nullptr. This // causes LLDrawPoolTerrain to use a blank paintmap instead. if (!success) { tex = nullptr; } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 8e7af28d41..a6d397c039 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -3171,22 +3171,32 @@ bool LLViewerShaderMgr::loadShadersInterface() } } - if (success) + if (gSavedSettings.getBOOL("LocalTerrainPaintEnabled")) { - LLGLSLShader* shader = &gTerrainStampProgram; - U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); - // LLTerrainPaintMap currently uses an RGB8 texture internally - bit_depth = llclamp(bit_depth, 1, 8); - shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); + if (success) + { + LLGLSLShader* shader = &gTerrainStampProgram; + U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); + // LLTerrainPaintMap currently uses an RGB8 texture internally + bit_depth = llclamp(bit_depth, 1, 8); + shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); - shader->mShaderFiles.clear(); - shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); - shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); - shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - const U32 value_range = (1 << bit_depth) - 1; - shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); - success = success && shader->createShader(); - llassert(success); + shader->mShaderFiles.clear(); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); + shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + const U32 value_range = (1 << bit_depth) - 1; + shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); + success = success && shader->createShader(); + //llassert(success); + if (!success) + { + LL_WARNS() << "Failed to create shader '" << shader->mName << "', disabling!" << LL_ENDL; + gSavedSettings.setBOOL("RenderCanUseTerrainBakeShaders", false); + // continue as if this shader never happened + success = true; + } + } } if (success) -- cgit v1.2.3 From 303bf1977b802f5a091bec66b65dfb6c2f29b761 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 7 Oct 2024 14:50:27 -0700 Subject: secondlife/viewer#1883: Fix terrain paintmap bit depth hardcoded in places and increase default bit depth to 8 --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llterrainpaintmap.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index babb8424dc..ebf1968007 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -15367,7 +15367,7 @@ Type U32 Value - 5 + 8 TerrainPaintResolution diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index d36e4ea657..ec40f299a4 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -573,7 +573,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur paint_out->mStartY = paint_in->mStartY; paint_out->mWidthX = paint_in->mWidthX; paint_out->mWidthY = paint_in->mWidthY; - paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later paint_out->mComponents = LLTerrainPaint::RGB; #ifdef SHOW_ASSERT paint_out->assert_confined_to(tex); @@ -602,7 +602,8 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // round-trip which will reduce the bit depth, making the // pre-conversion step not necessary. queue_out.enqueue(paint_out); - queue_out.convertBitDepths(queue_out.size()-1, 5); + LLCachedControl bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); } queue_in.clear(); @@ -775,7 +776,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe const F32 dY = brush_start_y - F32(paint_out->mStartY); paint_out->mWidthX = U16(ceil(brush_width_x + dX)); paint_out->mWidthY = U16(ceil(brush_width_y + dY)); - paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later paint_out->mComponents = LLTerrainPaint::RGB; // The brush strokes are expected to sometimes partially venture // outside of the paintmap bounds. @@ -803,7 +804,8 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe // round-trip which will reduce the bit depth, making the // pre-conversion step not necessary. queue_out.enqueue(paint_out); - queue_out.convertBitDepths(queue_out.size()-1, 5); + LLCachedControl bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); } queue_in.clear(); -- cgit v1.2.3 From d505eb4fce23e573c5155d1a9ba2857f549ca281 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 7 Oct 2024 15:57:50 -0700 Subject: secondlife/viewer#1883: Fix logic error caught by clang --- indra/newview/llterrainpaintmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index ec40f299a4..09caba8faa 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -846,7 +846,7 @@ bool LLTerrainQueue::enqueue(std::vector>& list) constexpr bool dry_run = true; for (auto& t : list) { - if (!enqueue(t), dry_run) { return false; } + if (!enqueue(t, dry_run)) { return false; } } for (auto& t : list) { -- cgit v1.2.3 From f88401c97a892ea4a6fce13ee15dad1bb976d992 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Fri, 11 Oct 2024 09:54:47 -0700 Subject: secondlife/viewer#1883: Review feedback --- indra/newview/llterrainpaintmap.cpp | 18 ++++++------------ indra/newview/llterrainpaintmap.h | 6 +++--- indra/newview/llvlcomposition.h | 2 -- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 09caba8faa..52376e3bf0 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -56,15 +56,15 @@ void check_tex(const LLViewerTexture& tex) llassert(tex.getPrimaryFormat() == GL_RGB); llassert(tex.getGLTexture()); } +#else +#define check_tex(tex) #endif } // namespace // static bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) { -#ifdef SHOW_ASSERT check_tex(tex); -#endif const LLSurface& surface = region.getLand(); const U32 patch_count = surface.getPatchesPerEdge(); @@ -309,9 +309,7 @@ void LLTerrainPaintMap::applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQ { if (queue.empty()) { return; } -#ifdef SHOW_ASSERT check_tex(tex); -#endif gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true); @@ -362,7 +360,7 @@ namespace // plane. // *NOTE: Because we know the vertex XY coordinates go from 0 to 1 // pre-transform, UVs can be calculated from the vertices -LLVertexBuffer& get_paint_triangle_buffer() +LLVertexBuffer* get_paint_triangle_buffer() { static LLPointer buf = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX); static bool initialized = false; @@ -395,7 +393,7 @@ LLVertexBuffer& get_paint_triangle_buffer() *(indices++) = 2; buf->unmapBuffer(); } - return *buf; + return buf.get(); } }; @@ -403,9 +401,7 @@ LLVertexBuffer& get_paint_triangle_buffer() // static LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in) { -#ifdef SHOW_ASSERT check_tex(tex); -#endif llassert(queue_in.getComponents() == LLTerrainPaint::RGBA); // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead @@ -429,7 +425,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; - LLVertexBuffer* buf = &get_paint_triangle_buffer(); + LLVertexBuffer* buf = get_paint_triangle_buffer(); // Update projection matrix and viewport // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. @@ -621,9 +617,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // static LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in) { -#ifdef SHOW_ASSERT check_tex(tex); -#endif // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead // TODO: even if it means performing extra render operations to apply the brushes, in rare cases where the paints can't all fit within an area that can be represented by the buffer @@ -646,7 +640,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; - LLVertexBuffer* buf = &get_paint_triangle_buffer(); + LLVertexBuffer* buf = get_paint_triangle_buffer(); // Update projection matrix and viewport // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index cffdad80a2..6c321dc9c6 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -26,7 +26,7 @@ #pragma once -#include "llviewerprecompiledheaders.h" +#include class LLTexture; class LLViewerRegion; @@ -90,8 +90,8 @@ struct LLTerrainPaint U16 mWidthY; U8 mBitDepth; U8 mComponents; - const static U8 RGB = 3; - const static U8 RGBA = 4; + static constexpr U8 RGB = 3; + static constexpr U8 RGBA = 4; std::vector mData; // Asserts that this paint's start/width fit within the bounds of the diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 9a5d74adda..21fd484375 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -27,8 +27,6 @@ #ifndef LL_LLVLCOMPOSITION_H #define LL_LLVLCOMPOSITION_H -#include - #include "llviewerlayer.h" #include "llviewershadermgr.h" #include "llviewertexture.h" -- cgit v1.2.3 From 887e417d3fc4af76b63911283cb183e36667e56b Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy Date: Sat, 12 Oct 2024 01:49:27 +0300 Subject: Revert "Add toggles to avatar dropdown for hear sound or voice from avatar. (#2518, #2519)" This reverts commit 6af471482d6801530915c1c9ae4bdf788af52eae. --- indra/newview/llagent.cpp | 14 -------------- indra/newview/llagent.h | 7 ------- indra/newview/llviewermenu.cpp | 2 -- indra/newview/skins/default/xui/en/menu_viewer.xml | 19 ------------------- 4 files changed, 42 deletions(-) diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index c8b0adbaf8..26c080bf89 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -355,20 +355,6 @@ bool LLAgent::isMicrophoneOn(const LLSD& sdname) return LLVoiceClient::getInstance()->getUserPTTState(); } -//static -void LLAgent::toggleHearMediaSoundFromAvatar() -{ - const S32 mediaSoundsEarLocation = gSavedSettings.getS32("MediaSoundsEarLocation"); - gSavedSettings.setS32("MediaSoundsEarLocation", !mediaSoundsEarLocation); -} - -//static -void LLAgent::toggleHearVoiceFromAvatar() -{ - const S32 voiceEarLocation = gSavedSettings.getS32("VoiceEarLocation"); - gSavedSettings.setS32("VoiceEarLocation", !voiceEarLocation); -} - // ************************************************************ // Enabled this definition to compile a 'hacked' viewer that // locally believes the end user has godlike powers. diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index afc34f747f..c1d3c6c14b 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -374,13 +374,6 @@ public: private: bool mVoiceConnected; - //-------------------------------------------------------------------- - // Sound - //-------------------------------------------------------------------- -public: - static void toggleHearMediaSoundFromAvatar(); - static void toggleHearVoiceFromAvatar(); - //-------------------------------------------------------------------- // Chat //-------------------------------------------------------------------- diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index df60130c9f..80c75ec919 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -9579,8 +9579,6 @@ void initialize_menus() commit.add("Agent.ToggleMicrophone", boost::bind(&LLAgent::toggleMicrophone, _2)); enable.add("Agent.IsMicrophoneOn", boost::bind(&LLAgent::isMicrophoneOn, _2)); enable.add("Agent.IsActionAllowed", boost::bind(&LLAgent::isActionAllowed, _2)); - commit.add("Agent.ToggleHearMediaSoundFromAvatar", boost::bind(&LLAgent::toggleHearMediaSoundFromAvatar)); - commit.add("Agent.ToggleHearVoiceFromAvatar", boost::bind(&LLAgent::toggleHearVoiceFromAvatar)); // File menu init_menu_file(); diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index a03024487f..e0e9fdcc32 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -561,25 +561,6 @@ parameter="conversation" /> - - - - - - - - - - Date: Sun, 13 Oct 2024 08:09:51 +0200 Subject: Remove traces of FLTK (#2834) --- indra/llwindow/llwindowsdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index f87a00c34b..9d736a9970 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -1369,7 +1369,7 @@ void LLWindowSDL::processMiscNativeEvents() void LLWindowSDL::gatherInput(bool app_has_focus) { - SDL_Event event; + SDL_Event event; // Handle all outstanding SDL events while (SDL_PollEvent(&event)) -- cgit v1.2.3 From 2252f0fc932e9cd3533c72b96bdb032b7ef89da1 Mon Sep 17 00:00:00 2001 From: Ansariel Hiller Date: Sun, 13 Oct 2024 11:01:38 +0200 Subject: Fix time format copy&paste error (#2844) --- .../newview/skins/default/xui/en/floater_inventory_item_properties.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml index cc7942abea..1aa216d6b4 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml @@ -30,7 +30,7 @@ - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] Date: Mon, 14 Oct 2024 13:17:23 +0300 Subject: #1921 Add setting to hide source info from output in Lua Debug Console --- indra/newview/app_settings/settings.xml | 11 +++++++++++ indra/newview/llfloaterluadebug.cpp | 4 +++- indra/newview/llluamanager.cpp | 9 +++++---- indra/newview/skins/default/xui/en/floater_lua_debug.xml | 11 +++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index d58ff17aaa..b11ba8955a 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4122,6 +4122,17 @@ Value + LuaDebugShowSource + + Comment + Show source info in Lua Debug Console output + Persist + 1 + Type + Boolean + Value + 0 + GridStatusRSS Comment diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index 06877df816..f7f3b8d588 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -58,7 +58,9 @@ bool LLFloaterLUADebug::postBuild() .listen("LLFloaterLUADebug", [mResultOutput=mResultOutput](const LLSD& data) { - mResultOutput->pasteTextWithLinebreaks(data.asString()); + LLCachedControl show_source_info(gSavedSettings, "LuaDebugShowSource", false); + std::string source_info = show_source_info ? data["source_info"].asString() : ""; + mResultOutput->pasteTextWithLinebreaks(data["level"].asString() + source_info + data["msg"].asString()); mResultOutput->addLineBreakChar(true); return false; }); diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 7fe5c1ece0..4a65276384 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -72,9 +72,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level) lluau_checkstack(L, 2); luaL_where(L, 1); // start with the 'where' info at the top of the stack - std::ostringstream out; - out << lua_tostring(L, -1); + std::string source_info{ lua_tostring(L, -1) }; lua_pop(L, 1); + + std::ostringstream out; const char* sep = ""; // 'where' info ends with ": " // now iterate over arbitrary args, calling Lua tostring() on each and // concatenating with separators @@ -101,10 +102,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level) // capture message string std::string msg{ out.str() }; // put message out there for any interested party (*koff* LLFloaterLUADebug *koff*) - LLEventPumps::instance().obtain("lua output").post(stringize(level, ": ", msg)); + LLEventPumps::instance().obtain("lua output").post(llsd::map("msg", msg, "level", stringize(level, ": "), "source_info", source_info)); llcoro::suspend(); - return msg; + return source_info + msg; } lua_function(print_debug, "print_debug(args...): DEBUG level logging") diff --git a/indra/newview/skins/default/xui/en/floater_lua_debug.xml b/indra/newview/skins/default/xui/en/floater_lua_debug.xml index 15027f1647..5efe1c958a 100644 --- a/indra/newview/skins/default/xui/en/floater_lua_debug.xml +++ b/indra/newview/skins/default/xui/en/floater_lua_debug.xml @@ -25,6 +25,17 @@ width="100"> LUA string: + Date: Fri, 11 Oct 2024 22:16:51 +0300 Subject: viewer#2819 Group member pagination toggle Group member pagination is not ready, disable it untill later --- indra/newview/app_settings/settings.xml | 11 +++++++++++ indra/newview/llpanelgrouproles.cpp | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 13ec35fa07..b7d1575bb1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2469,6 +2469,17 @@ Value 1 + UseGroupMemberPagination + + Comment + Enable pagination of group memeber list 50 members at a time. + Persist + 1 + Type + Boolean + Value + 0 + DisplayTimecode Comment diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 4404efff98..e1f2d7588c 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -1360,7 +1360,8 @@ void LLPanelGroupMembersSubTab::activate() { if (!gdatap || !gdatap->isMemberDataComplete()) { - const U32 page_size = 50; + static LLCachedControl enable_pagination(gSavedSettings, "UseGroupMemberPagination", false); + const U32 page_size = enable_pagination() ? 50 : 0; std::string sort_column_name = mMembersList->getSortColumnName(); bool sort_descending = !mMembersList->getSortAscending(); LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID, page_size, 0, sort_column_name, sort_descending); -- cgit v1.2.3 From 1983342a0578a32a2a5f148514e708d3be27d13f Mon Sep 17 00:00:00 2001 From: Ansariel Hiller Date: Mon, 14 Oct 2024 18:06:04 +0200 Subject: Use correct German date format for German localization (#2845) --- indra/newview/skins/default/xui/de/floater_about_land.xml | 3 ++- indra/newview/skins/default/xui/de/floater_inspect.xml | 5 ++++- indra/newview/skins/default/xui/de/panel_classified_info.xml | 2 +- indra/newview/skins/default/xui/de/panel_landmark_info.xml | 5 ++++- indra/newview/skins/default/xui/de/panel_place_profile.xml | 5 ++++- indra/newview/skins/default/xui/de/panel_profile_classified.xml | 2 +- indra/newview/skins/default/xui/de/panel_profile_secondlife.xml | 6 ++++++ indra/newview/skins/default/xui/de/panel_status_bar.xml | 2 +- indra/newview/skins/default/xui/de/sidepanel_item_info.xml | 8 ++++---- indra/newview/skins/default/xui/de/strings.xml | 8 ++++---- 10 files changed, 31 insertions(+), 15 deletions(-) diff --git a/indra/newview/skins/default/xui/de/floater_about_land.xml b/indra/newview/skins/default/xui/de/floater_about_land.xml index bb9ab26ef5..9c93ff38ad 100644 --- a/indra/newview/skins/default/xui/de/floater_about_land.xml +++ b/indra/newview/skins/default/xui/de/floater_about_land.xml @@ -25,7 +25,8 @@ (keiner) (Wird verkauft) Keine Parzelle ausgewählt. - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] Name: Beschreibung: Typ: diff --git a/indra/newview/skins/default/xui/de/floater_inspect.xml b/indra/newview/skins/default/xui/de/floater_inspect.xml index da97ceb2d8..a193e1123e 100644 --- a/indra/newview/skins/default/xui/de/floater_inspect.xml +++ b/indra/newview/skins/default/xui/de/floater_inspect.xml @@ -1,7 +1,10 @@ + + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt] + - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] diff --git a/indra/newview/skins/default/xui/de/panel_classified_info.xml b/indra/newview/skins/default/xui/de/panel_classified_info.xml index 007e9d69f0..1b8caf5f78 100644 --- a/indra/newview/skins/default/xui/de/panel_classified_info.xml +++ b/indra/newview/skins/default/xui/de/panel_classified_info.xml @@ -13,7 +13,7 @@ [TELEPORT] teleportieren, [MAP] Karte, [PROFILE] Profil - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] + [day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt] Aktiviert diff --git a/indra/newview/skins/default/xui/de/panel_landmark_info.xml b/indra/newview/skins/default/xui/de/panel_landmark_info.xml index 10cf34c170..8726d5e645 100644 --- a/indra/newview/skins/default/xui/de/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/de/panel_landmark_info.xml @@ -15,8 +15,11 @@ Die Informationen über diesen Standort sind zugriffsbeschränkt. Bitte wenden Sie sich bezüglich Ihrer Berechtigungen an den Eigentümer der Parzelle. + + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] + - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]