diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index f80c1a15e..fc7b0a878 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -30,8 +30,6 @@ include(BuildVersion) include(UnixInstall) -set (DISABLE_FATAL_WARNINGS CACHE BOOL TRUE) - if (NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type. One of: Debug Release RelWithDebInfo" FORCE) diff --git a/indra/aistatemachine/aistatemachine.h b/indra/aistatemachine/aistatemachine.h index 160c6d6eb..9b5b0aab6 100644 --- a/indra/aistatemachine/aistatemachine.h +++ b/indra/aistatemachine/aistatemachine.h @@ -146,12 +146,15 @@ class AIStateMachine : public LLThreadSafeRefCount typedef AIAccessConst multiplex_state_type_crat; typedef AIAccess multiplex_state_type_rat; typedef AIAccess multiplex_state_type_wat; + + protected: // Sub state. AIThreadSafeSimpleDC mSubState; typedef AIAccessConst sub_state_type_crat; typedef AIAccess sub_state_type_rat; typedef AIAccess sub_state_type_wat; + private: // Mutex protecting everything below and making sure only one thread runs the state machine at a time. LLMutex mMultiplexMutex; // Mutex that is locked while calling *_impl() functions and the call back. diff --git a/indra/aistatemachine/aistatemachinethread.cpp b/indra/aistatemachine/aistatemachinethread.cpp index 030bd0a5a..f45d1e845 100644 --- a/indra/aistatemachine/aistatemachinethread.cpp +++ b/indra/aistatemachine/aistatemachinethread.cpp @@ -33,12 +33,12 @@ class AIStateMachineThreadBase::Thread : public LLThread { private: - LLPointer mImpl; + LLPointer mImpl; bool mNeedCleanup; public: - Thread(AIThreadImpl* impl) : + Thread(AIStateMachineThreadBase* impl) : #ifdef LL_DEBUG - LLThread(impl->getName()), + LLThread(impl->impl().getName()), #else LLThread("AIStateMachineThreadBase::Thread"), #endif @@ -46,7 +46,7 @@ class AIStateMachineThreadBase::Thread : public LLThread { protected: /*virtual*/ void run(void) { - mNeedCleanup = mImpl->thread_done(mImpl->run()); + mNeedCleanup = mImpl->impl().thread_done(mImpl->impl().run()); } /*virtual*/ void terminated(void) { @@ -65,7 +65,7 @@ class AIStateMachineThreadBase::Thread : public LLThread { } public: // TODO: Implement a thread pool. For now, just create a new one every time. - static Thread* allocate(AIThreadImpl* impl) { return new Thread(impl); } + static Thread* allocate(AIStateMachineThreadBase* impl) { return new Thread(impl); } static void completed(Thread* threadp) { delete threadp; } }; @@ -93,7 +93,7 @@ void AIStateMachineThreadBase::multiplex_impl(state_type run_state) switch(run_state) { case start_thread: - mThread = Thread::allocate(mImpl); + mThread = Thread::allocate(this); // Set next state. set_state(wait_stopped); idle(); // Wait till the thread returns. @@ -124,10 +124,10 @@ void AIStateMachineThreadBase::abort_impl(void) { if (mThread) { - // If this AIStateMachineThreadBase still exists then the first base class of - // AIStateMachineThread, LLPointer, also still exists - // and therefore mImpl is valid. - bool need_cleanup = mImpl->state_machine_done(mThread); // Signal the fact that we aborted. + // If this AIStateMachineThreadBase still exists then the AIStateMachineThread + // that is derived from it still exists and therefore its member THREAD_IMPL also still exists + // and therefore impl() is valid. + bool need_cleanup = impl().state_machine_done(mThread); // Signal the fact that we aborted. if (need_cleanup) { // This is an unlikely race condition. We have been aborted by our parent, diff --git a/indra/aistatemachine/aistatemachinethread.h b/indra/aistatemachine/aistatemachinethread.h index 85b2ff108..780060bb6 100644 --- a/indra/aistatemachine/aistatemachinethread.h +++ b/indra/aistatemachine/aistatemachinethread.h @@ -73,11 +73,11 @@ enum hello_world_state_type { // The statemachine class (this is almost a template). class HelloWorld : public AIStateMachine { private: - AIStateMachineThread mHelloWorld; + LLPointer > mHelloWorld; bool mErr; public: - HelloWorld() : mErr(false) { } + HelloWorld() : mHelloWorld(new AIStateMachineThread), mErr(false) { } // Print to stderr or stdout? void init(bool err) { mErr = err; } @@ -108,7 +108,7 @@ class HelloWorld : public AIStateMachine { void HelloWorld::initialize_impl(void) { - mHelloWorld->init(mErr); // Initialize the thread object. + mHelloWorld->thread_impl().init(mErr); // Initialize the thread object. set_state(HelloWorld_start); } @@ -118,14 +118,14 @@ void HelloWorld::multiplex_impl(state_type run_state) { case HelloWorld_start: { - mHelloWorld.run(this, HelloWorld_done); // Run HelloWorldThread and set the state of 'this' to HelloWorld_done when finished. + mHelloWorld->run(this, HelloWorld_done); // Run HelloWorldThread and set the state of 'this' to HelloWorld_done when finished. idle(HelloWorld_start); // Always go idle after starting a thread! break; } case HelloWorld_done: { // We're done. Lets also abort when the thread reported no success. - if (mHelloWorld->successful()) // Read output/result of thread object. + if (mHelloWorld->thread_impl().successful()) // Read output/result of thread object. finish(); else abort(); @@ -139,9 +139,9 @@ void HelloWorld::multiplex_impl(state_type run_state) class AIStateMachineThreadBase; // Derive from this to implement the code that must run in another thread. -class AIThreadImpl : public LLThreadSafeRefCount { +class AIThreadImpl { private: - template friend struct AIStateMachineThread; + template friend class AIStateMachineThread; typedef AIAccess StateMachineThread_wat; AIThreadSafeSimpleDC mStateMachineThread; @@ -158,6 +158,9 @@ class AIThreadImpl : public LLThreadSafeRefCount { public: char const* getName(void) const { return mName; } #endif + + protected: + virtual ~AIThreadImpl() { } }; // The base class for statemachine threads. @@ -178,7 +181,7 @@ class AIStateMachineThreadBase : public AIStateMachine { static state_type const max_state = wait_stopped + 1; protected: - AIStateMachineThreadBase(AIThreadImpl* impl) : mImpl(impl) { ref(); /* Never call delete */ } + AIStateMachineThreadBase(void) { } private: // Handle initializing the object. @@ -193,9 +196,11 @@ class AIStateMachineThreadBase : public AIStateMachine { // Implemenation of state_str for run states. /*virtual*/ char const* state_str_impl(state_type run_state) const; + // Returns a reference to the implementation code that needs to be run in the thread. + virtual AIThreadImpl& impl(void) = 0; + private: Thread* mThread; // The thread that the code is run in. - AIThreadImpl* mImpl; // Pointer to the implementation code that needs to be run in the thread. bool mAbort; // (Inverse of) return value of AIThreadImpl::run(). Only valid in state wait_stopped. public: @@ -206,14 +211,22 @@ class AIStateMachineThreadBase : public AIStateMachine { // The state machine that runs T::run() in a thread. // THREAD_IMPL Must be derived from AIThreadImpl. template -struct AIStateMachineThread : public LLPointer, public AIStateMachineThreadBase { - // Constructor. - AIStateMachineThread(void) : - LLPointer(new THREAD_IMPL), - AIStateMachineThreadBase(LLPointer::get()) - { - *AIThreadImpl::StateMachineThread_wat(static_cast(LLPointer::get())->mStateMachineThread) = this; - } +class AIStateMachineThread : public AIStateMachineThreadBase { + private: + THREAD_IMPL mThreadImpl; + + public: + // Constructor. + AIStateMachineThread(void) + { + *AIThreadImpl::StateMachineThread_wat(mThreadImpl.mStateMachineThread) = this; + } + + // Accessor. + THREAD_IMPL& thread_impl(void) { return mThreadImpl; } + + protected: + /*virtual*/ AIThreadImpl& impl(void) { return mThreadImpl; } }; #endif diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 5eb78b0b2..449186a08 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -270,10 +270,10 @@ endif (DARWIN) if (LINUX OR DARWIN) if(${CMAKE_C_COMPILER} MATCHES "gcc*") - set(UNIX_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs") + set(UNIX_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs") set(UNIX_CXX_WARNINGS "${UNIX_WARNINGS} -Wno-reorder -Wno-non-virtual-dtor -Woverloaded-virtual") elseif(${CMAKE_C_COMPILER} MATCHES "clang*") - set(UNIX_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs -Wno-tautological-compare -Wno-char-subscripts -Wno-gnu -Wno-logical-op-parentheses -Wno-non-virtual-dtor ") + set(UNIX_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs -Wno-tautological-compare -Wno-char-subscripts -Wno-gnu -Wno-logical-op-parentheses -Wno-non-virtual-dtor") set(UNIX_WARNINGS "${UNIX_WARNINGS} -Woverloaded-virtual -Wno-parentheses-equality -Wno-reorder -Wno-unused-function -Wno-unused-value -Wno-unused-variable") set(UNIX_CXX_WARNINGS "${UNIX_WARNINGS}") elseif(${CMAKE_C_COMPILER} MATCHES "icc") @@ -281,8 +281,11 @@ if (LINUX OR DARWIN) set(UNIX_CXX_WARNINGS "${UNIX_WARNINGS}") endif() + # Use -DDISABLE_FATAL_WARNINGS:BOOL=FALSE during configuration to enable fatal warnings. + set(DISABLE_FATAL_WARNINGS TRUE CACHE BOOL "Set this to FALSE to enable fatal warnings.") if (NOT DISABLE_FATAL_WARNINGS) set(UNIX_WARNINGS "${UNIX_WARNINGS} -Werror") + set(UNIX_CXX_WARNINGS "${UNIX_CXX_WARNINGS} -Werror") endif (NOT DISABLE_FATAL_WARNINGS) set(CMAKE_C_FLAGS "${UNIX_WARNINGS} ${CMAKE_C_FLAGS}") @@ -333,4 +336,6 @@ MARK_AS_ADVANCED( CMAKE_SHARED_LINKER_FLAGS_RELEASE ) +include(GooglePerfTools) + endif(NOT DEFINED ${CMAKE_CURRENT_LIST_FILE}_INCLUDED) diff --git a/indra/cmake/GooglePerfTools.cmake b/indra/cmake/GooglePerfTools.cmake index 3ad669c94..d92c39ded 100644 --- a/indra/cmake/GooglePerfTools.cmake +++ b/indra/cmake/GooglePerfTools.cmake @@ -1,4 +1,5 @@ # -*- cmake -*- + include(Prebuilt) if(WORD_SIZE EQUAL 64) @@ -47,9 +48,14 @@ else (USE_GOOGLE_PERFTOOLS) endif (USE_GOOGLE_PERFTOOLS) if (NOT(DISABLE_TCMALLOC OR USE_GOOGLE_PERFTOOLS OR STANDALONE)) - message(STATUS "Building with Google TCMalloc") - set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1) - include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR}) - set(GOOGLE_PERFTOOLS_LIBRARIES ${TCMALLOC_LIBRARIES}) + if (NOT STATUS_Building_with_Google_TCMalloc) + message(STATUS "Building with Google TCMalloc") + set(STATUS_Building_with_Google_TCMalloc true PARENT_SCOPE) + endif (NOT STATUS_Building_with_Google_TCMalloc) + set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1) + include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR}) + set(GOOGLE_PERFTOOLS_LIBRARIES ${TCMALLOC_LIBRARIES}) set(GOOGLE_PERFTOOLS_LINKER_FLAGS ${TCMALLOC_LINKER_FLAGS}) endif() + +add_definitions(${TCMALLOC_FLAG}) \ No newline at end of file diff --git a/indra/develop.py b/indra/develop.py index a2215b3b0..853c37e14 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -45,7 +45,6 @@ import commands class CommandError(Exception): pass - def mkdir(path): try: os.mkdir(path) @@ -54,15 +53,19 @@ def mkdir(path): if err.errno != errno.EEXIST or not os.path.isdir(path): raise -def getcwd(): - cwd = os.getcwd() - if 'a' <= cwd[0] <= 'z' and cwd[1] == ':': +def prettyprint_path_for_cmake(path): + if 'a' <= path[0] <= 'z' and path[1] == ':': # CMake wants DOS drive letters to be in uppercase. The above # condition never asserts on platforms whose full path names # always begin with a slash, so we don't need to test whether # we are running on Windows. - cwd = cwd[0].upper() + cwd[1:] - return cwd + path = path[0].upper() + path[1:] + return path + +def getcwd(): + return prettyprint_path_for_cmake(os.getcwd()) + +source_indra = prettyprint_path_for_cmake(os.path.dirname(os.path.realpath(__file__))) def quote(opts): return '"' + '" "'.join([ opt.replace('"', '') for opt in opts ]) + '"' @@ -150,7 +153,7 @@ class PlatformSetup(object): simple = False try: os.chdir(d) - cmd = self.cmake_commandline(cwd, d, args, simple) + cmd = self.cmake_commandline(source_indra, d, args, simple) print 'Running %r in %r' % (cmd, d) self.run(cmd, 'cmake') finally: @@ -270,18 +273,9 @@ class LinuxSetup(UnixSetup): return 'linux' def build_dirs(self): - # Only build the server code if we have it. platform_build = '%s-%s' % (self.platform(), self.build_type.lower()) - if self.arch() == 'i686' and self.is_internal_tree(): - return ['viewer-' + platform_build, 'server-' + platform_build] - elif self.arch() == 'x86_64' and self.is_internal_tree(): - # the viewer does not build in 64bit -- kdu5 issues - # we can either use openjpeg, or overhaul our viewer to handle kdu5 or higher - # doug knows about kdu issues - return ['server-' + platform_build] - else: - return ['viewer-' + platform_build] + return ['viewer-' + platform_build] def cmake_commandline(self, src_dir, build_dir, opts, simple): args = dict( @@ -293,31 +287,11 @@ class LinuxSetup(UnixSetup): type=self.build_type.upper(), project_name=self.project_name, word_size=self.word_size, + cxx="g++" ) - if not self.is_internal_tree(): - args.update({'cxx':'g++', 'server':'OFF', 'viewer':'ON'}) - else: - if self.distcc: - distcc = self.find_in_path('distcc') - baseonly = True - else: - distcc = [] - baseonly = False - if 'server' in build_dir: - gcc = distcc + self.find_in_path( - self.debian_sarge and 'g++-3.3' or 'g++-4.1', - 'g++', baseonly) - args.update({'cxx': ' '.join(gcc), 'server': 'ON', - 'viewer': 'OFF'}) - else: - gcc41 = distcc + self.find_in_path('g++-4.1', 'g++', baseonly) - args.update({'cxx': ' '.join(gcc41), - 'server': 'OFF', - 'viewer': 'ON'}) + cmd = (('cmake -DCMAKE_BUILD_TYPE:STRING=%(type)s ' - '-G %(generator)r -DSERVER:BOOL=%(server)s ' - '-DVIEWER:BOOL=%(viewer)s -DSTANDALONE:BOOL=%(standalone)s ' - '-DUNATTENDED:BOOL=%(unattended)s ' + '-G %(generator)r -DSTANDALONE:BOOL=%(standalone)s ' '-DWORD_SIZE:STRING=%(word_size)s ' '-DROOT_PROJECT_NAME:STRING=%(project_name)s ' '%(opts)s %(dir)r') diff --git a/indra/libhacd/CMakeLists.txt b/indra/libhacd/CMakeLists.txt index e346bc9b8..59aad8a23 100644 --- a/indra/libhacd/CMakeLists.txt +++ b/indra/libhacd/CMakeLists.txt @@ -1,13 +1,39 @@ -cmake_minimum_required(VERSION 2.6.4) +# -*- cmake -*- -project(libhacd CXX C) +project(libhacd) include(00-Common) -file (GLOB SOURCE_FILES *.cpp ) -file (GLOB INCLUDE_FILES *.h ) +set(libhacd_SOURCE_FILES + hacdGraph.cpp + hacdHACD.cpp + hacdICHull.cpp + hacdManifoldMesh.cpp + hacdMeshDecimator.cpp + hacdMicroAllocator.cpp + hacdRaycastMesh.cpp +) + +set(libhacd_HEADER_FILES + hacdCircularList.h + hacdCircularList.inl + hacdGraph.h + hacdHACD.h + hacdICHull.h + hacdManifoldMesh.h + hacdMeshDecimator.h + hacdMicroAllocator.h + hacdRaycastMesh.h + hacdSArray.h + hacdVector.h + hacdVector.inl + hacdVersion.h +) + +set_source_files_properties(${libhacd_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) IF(WINDOWS) add_definitions(-D_CRT_SECURE_NO_WARNINGS) ENDIF(WINDOWS) -add_library(hacd ${SOURCE_FILES} ${INCLUDE_FILES}) +add_library(hacd ${libhacd_SOURCE_FILES} ${libhacd_INCLUDE_FILES}) diff --git a/indra/libhacd/hacdHACD.h b/indra/libhacd/hacdHACD.h index bdbbc472b..0c9f11653 100644 --- a/indra/libhacd/hacdHACD.h +++ b/indra/libhacd/hacdHACD.h @@ -76,6 +76,7 @@ namespace HACD { public: virtual void operator()( char const *aMsg, double aProgress, double aConcavity, size_t aVertices) = 0; + virtual ~ICallback() {} }; typedef ICallback* CallBackFunction; diff --git a/indra/libndhacd/CMakeLists.txt b/indra/libndhacd/CMakeLists.txt index 93cf90a13..bcc1973c0 100644 --- a/indra/libndhacd/CMakeLists.txt +++ b/indra/libndhacd/CMakeLists.txt @@ -1,9 +1,35 @@ +# -*- cmake -*- + project(libndhacd) +include(00-Common) + include_directories(${LIBS_OPEN_DIR}/libhacd) -set (SOURCE_FILES LLConvexDecomposition.cpp nd_hacdConvexDecomposition.cpp nd_hacdStructs.cpp nd_hacdUtils.cpp nd_EnterExitTracer.cpp nd_StructTracer.cpp ) -file(GLOB HEADER_FILES *.h) +set (libndhacd_SOURCE_FILES + LLConvexDecomposition.cpp + nd_hacdConvexDecomposition.cpp + nd_hacdStructs.cpp + nd_hacdUtils.cpp + nd_EnterExitTracer.cpp + nd_StructTracer.cpp +) -add_library( nd_hacdConvexDecomposition STATIC ${SOURCE_FILES} ${HEADER_FILES}) +set (libndhacd_HEADER_FILES + LLConvexDecomposition.h + ndConvexDecomposition.h + nd_hacdConvexDecomposition.h + nd_hacdStructs.h + nd_StructTracer.h + LLConvexDecompositionStubImpl.h + nd_EnterExitTracer.h + nd_hacdDefines.h + nd_hacdUtils.h + windowsincludes.h +) + +set_source_files_properties(${libndhacd_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +add_library( nd_hacdConvexDecomposition STATIC ${libndhacd_SOURCE_FILES} ${libndhacd_HEADER_FILES}) diff --git a/indra/libpathing/CMakeLists.txt b/indra/libpathing/CMakeLists.txt index 087faec05..aea1212e5 100644 --- a/indra/libpathing/CMakeLists.txt +++ b/indra/libpathing/CMakeLists.txt @@ -1,13 +1,24 @@ -cmake_minimum_required(VERSION 2.6.4) +# -*- cmake -*- -project(ndPathingLib CXX C) +project(libpathing) +include(00-Common) if( MSVC ) add_definitions(-D_SECURE_SCL=0 -D_CRT_SECURE_NO_WARNINGS=1) endif( MSVC ) -file (GLOB SOURCE_FILES *.cpp ) -file (GLOB INCLUDE_FILES *.h ) +set(libpathing_SOURCE_FILES + llpathinglib.cpp + llphysicsextensions.cpp +) -add_library(nd_Pathing STATIC ${SOURCE_FILES} ${INCLUDE_FILES} ) +set(libpathing_HEADER_FILES + llpathinglib.h + llphysicsextensions.h +) + +set_source_files_properties(${libpathing_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +add_library(nd_Pathing STATIC ${libpathing_SOURCE_FILES} ${libpathing_HEADER_FILES} ) diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 984e63ca9..4b4108701 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -152,8 +152,8 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) << ")! You should be using FMOD Ex" << FMOD_VERSION << LL_ENDL; } - result = mSystem->setSoftwareFormat(44100, FMOD_SOUND_FORMAT_PCM16, 0, 0, FMOD_DSP_RESAMPLER_LINEAR); - Check_FMOD_Error(result,"FMOD::System::setSoftwareFormat"); +// result = mSystem->setSoftwareFormat(44100, FMOD_SOUND_FORMAT_PCM16, 0, 0, FMOD_DSP_RESAMPLER_LINEAR); +// Check_FMOD_Error(result,"FMOD::System::setSoftwareFormat"); // In this case, all sounds, PLUS wind and stream will be software. result = mSystem->setSoftwareChannels(num_channels + 2); diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp index 2f7bdf6bd..6f6973209 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.cpp +++ b/indra/llaudio/llstreamingaudio_fmodex.cpp @@ -405,7 +405,7 @@ LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, con exinfo.cbsize = sizeof(exinfo); exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_OGGVORBIS; //Hint to speed up loading.*/ - FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_MPEGSEARCH | FMOD_IGNORETAGS, 0, &mInternetStream); + FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, 0, &mInternetStream); if (result!= FMOD_OK) { diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index a0a940a9e..2de13aa36 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -40,7 +40,6 @@ #include #include -#include "lluuidhashmap.h" #include "llmotion.h" #include "llpose.h" #include "llframetimer.h" diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index a6da6f0c3..ac958ef0e 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -234,7 +234,7 @@ set(llcommon_HEADER_FILES lltypeinfolookup.h lluri.h lluuid.h - lluuidhashmap.h + sguuidhash.h llversionviewer.h.in llworkerthread.h metaclass.h diff --git a/indra/llcommon/aiframetimer.h b/indra/llcommon/aiframetimer.h index cb50caec5..366581c49 100644 --- a/indra/llcommon/aiframetimer.h +++ b/indra/llcommon/aiframetimer.h @@ -67,7 +67,7 @@ class LL_COMMON_API AIFrameTimer mutable Signal* mCallback; // Pointer to callback struct, or NULL when the object wasn't added to sTimerList yet. public: - AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mCallback(NULL), mTimer(timer) { } + AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mTimer(timer), mCallback(NULL) { } ~AIRunningFrameTimer() { delete mCallback; } // This function is called after the final object was added to sTimerList (where it is initialized in-place). @@ -89,7 +89,7 @@ class LL_COMMON_API AIFrameTimer #if LL_DEBUG // May not copy this object after it was initialized. AIRunningFrameTimer(AIRunningFrameTimer const& running_frame_timer) : - mExpire(running_frame_timer.mExpire), mCallback(running_frame_timer.mCallback), mTimer(running_frame_timer.mTimer) + mExpire(running_frame_timer.mExpire), mTimer(running_frame_timer.mTimer), mCallback(running_frame_timer.mCallback) { llassert(!mCallback); } #endif }; diff --git a/indra/llcommon/aithreadid.h b/indra/llcommon/aithreadid.h index cc7197b21..6b2c08516 100644 --- a/indra/llcommon/aithreadid.h +++ b/indra/llcommon/aithreadid.h @@ -33,7 +33,7 @@ #include // apr_os_thread_t, apr_os_thread_current(), apr_os_thread_equal(). #include // std::ostream. -#include "llpreprocessor.h" // LL_COMMON_API, LL_COMMON_API_TLS +#include "llpreprocessor.h" // LL_COMMON_API, LL_COMMON_API_TLS, LL_UNLIKELY // Lightweight wrapper around apr_os_thread_t. // This class introduces no extra assembly code after optimization; it's only intend is to provide type-safety. @@ -87,6 +87,16 @@ public: #endif }; +// Debugging function. +inline bool is_single_threaded(AIThreadID& thread_id) +{ + if (LL_UNLIKELY(thread_id.is_no_thread())) + { + thread_id.reset(); + } + return thread_id.equals_current_thread(); +} + // Legacy function. inline bool is_main_thread(void) { diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 6d2bb8f13..12eb04932 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -95,7 +95,19 @@ public: namespace { #if LL_WINDOWS -typedef std::filebuf _Myfb; +//typedef std::filebuf _Myfb; +//Singu note: Wrap around std::filebuf to override the open procedure. +// The client encodes filepaths in UTF-8, however Windows uses UTF-16 encoding natively. +// Need to convert paths to UTF-16 before calling std::filebuf::open. +struct _Myfb : public std::filebuf +{ + _Myfb() : std::filebuf() {} + _Myfb(_Filet* file) : std::filebuf(file) {} + _Myt *open(const char *filename, std::ios_base::openmode mode, int prot = (int)std::ios_base::_Openprot) + { + return std::filebuf::open(utf8str_to_utf16str(filename).c_str(),mode,prot); + } +}; #else typedef __gnu_cxx::stdio_filebuf< char > _Myfb; typedef std::__c_file _Filet; diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 8a0e0e66d..0762dc3f4 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -57,13 +57,12 @@ inline void ll_aligned_free( void* ptr ) free( ((void**)ptr)[-1] ); } -#if !LL_USE_TCMALLOC inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed with ll_aligned_free_16(). { -#if defined(LL_WINDOWS) - return _aligned_malloc(size, 16); -#elif defined(LL_DARWIN) +#if (LL_DARWIN || LL_USE_TCMALLOC) return malloc(size); // default osx malloc is 16 byte aligned. +#elif LL_WINDOWS + return _aligned_malloc(size, 16); #else void *rtn; if (LL_LIKELY(0 == posix_memalign(&rtn, 16, size))) @@ -75,10 +74,10 @@ inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed wi inline void ll_aligned_free_16(void *p) { -#if defined(LL_WINDOWS) +#if (LL_DARWIN || LL_USE_TCMALLOC) + free(p); +#elif LL_WINDOWS _aligned_free(p); -#elif defined(LL_DARWIN) - return free(p); #else free(p); // posix_memalign() is compatible with heap deallocator #endif @@ -86,10 +85,10 @@ inline void ll_aligned_free_16(void *p) inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // returned hunk MUST be freed with ll_aligned_free_16(). { -#if defined(LL_WINDOWS) - return _aligned_realloc(ptr, size, 16); -#elif defined(LL_DARWIN) +#if (LL_DARWIN || LL_USE_TCMALLOC) return realloc(ptr,size); // default osx malloc is 16 byte aligned. +#elif LL_WINDOWS + return _aligned_realloc(ptr, size, 16); #else //FIXME: memcpy is SLOW void* ret = ll_aligned_malloc_16(size); @@ -106,18 +105,11 @@ inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // r #endif } -#else // USE_TCMALLOC -// ll_aligned_foo_16 are not needed with tcmalloc -#define ll_aligned_malloc_16 malloc -#define ll_aligned_realloc_16(a,b,c) realloc(a,b) -#define ll_aligned_free_16 free -#endif // USE_TCMALLOC - inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed with ll_aligned_free_32(). { -#if defined(LL_WINDOWS) +#if LL_WINDOWS return _aligned_malloc(size, 32); -#elif defined(LL_DARWIN) +#elif LL_DARWIN return ll_aligned_malloc( size, 32 ); #else void *rtn; @@ -130,9 +122,9 @@ inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed wi inline void ll_aligned_free_32(void *p) { -#if defined(LL_WINDOWS) +#if LL_WINDOWS _aligned_free(p); -#elif defined(LL_DARWIN) +#elif LL_DARWIN ll_aligned_free( p ); #else free(p); // posix_memalign() is compatible with heap deallocator diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index 105b69d34..9f49fcbf5 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -203,7 +203,6 @@ inline T* get_ptr_in_map(const std::map& inmap, const K& key) template inline bool is_in_map(const std::map& inmap, const K& key) { - typedef typename std::map::const_iterator map_iter; if(inmap.find(key) == inmap.end()) { return false; diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 8a2b6d0b5..a879485ad 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -403,8 +403,8 @@ void LLCondition::broadcast() //============================================================================ LLMutexBase::LLMutexBase() : - mLockingThread(AIThreadID::sNone), - mCount(0) + mCount(0), + mLockingThread(AIThreadID::sNone) { } diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp index 7e5d648fa..e77d8b63a 100644 --- a/indra/llcommon/lltimer.cpp +++ b/indra/llcommon/lltimer.cpp @@ -251,7 +251,7 @@ U64 totalTime() } else { - if (current_clock_count >= gLastTotalTimeClockCount) + if (LL_LIKELY(current_clock_count >= gLastTotalTimeClockCount)) { // No wrapping, we're all okay. gTotalTimeClockCount += current_clock_count - gLastTotalTimeClockCount; diff --git a/indra/llcommon/lluuidhashmap.h b/indra/llcommon/lluuidhashmap.h deleted file mode 100644 index c45e41399..000000000 --- a/indra/llcommon/lluuidhashmap.h +++ /dev/null @@ -1,589 +0,0 @@ -/** - * @file lluuidhashmap.h - * @brief A uuid based hash map. - * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLUUIDHASHMAP_H -#define LL_LLUUIDHASHMAP_H - -#include "stdtypes.h" -#include "llerror.h" -#include "lluuid.h" - -// UUID hash map - - /* - LLUUIDHashMap foo(test_equals); - LLUUIDHashMapIter bar(&foo); - - LLDynamicArray source_ids; - const S32 COUNT = 100000; - S32 q; - for (q = 0; q < COUNT; q++) - { - llinfos << "Creating" << llendl; - LLUUID id; - id.generate(); - //llinfos << q << ":" << id << llendl; - uuid_pair pair; - pair.mUUID = id; - pair.mValue = q; - foo.set(id, pair); - source_ids.put(id); - //ms_sleep(1); - } - - uuid_pair cur; - llinfos << "Iterating" << llendl; - for (cur = bar.first(); !bar.done(); cur = bar.next()) - { - if (source_ids[cur.mValue] != cur.mUUID) - { - llerrs << "Incorrect value iterated!" << llendl; - } - //llinfos << cur.mValue << ":" << cur.mUUID << llendl; - //ms_sleep(1); - } - - llinfos << "Finding" << llendl; - for (q = 0; q < COUNT; q++) - { - cur = foo.get(source_ids[q]); - if (source_ids[cur.mValue] != cur.mUUID) - { - llerrs << "Incorrect value found!" << llendl; - } - //llinfos << res.mValue << ":" << res.mUUID << llendl; - //ms_sleep(1); - } - - llinfos << "Removing" << llendl; - for (q = 0; q < COUNT/2; q++) - { - if (!foo.remove(source_ids[q])) - { - llerrs << "Remove failed!" << llendl; - } - //ms_sleep(1); - } - - llinfos << "Iterating" << llendl; - for (cur = bar.first(); !bar.done(); cur = bar.next()) - { - if (source_ids[cur.mValue] != cur.mUUID) - { - llerrs << "Incorrect value found!" << llendl; - } - //llinfos << cur.mValue << ":" << cur.mUUID << llendl; - //ms_sleep(1); - } - llinfos << "Done with UUID map test" << llendl; - - return 0; - */ - - -// -// LLUUIDHashNode -// - -template -class LLUUIDHashNode -{ -public: - LLUUIDHashNode(); - -public: - S32 mCount; - U8 mKey[SIZE]; - DATA mData[SIZE]; - LLUUIDHashNode *mNextNodep; -}; - - -// -// LLUUIDHashNode implementation -// -template -LLUUIDHashNode::LLUUIDHashNode() -{ - mCount = 0; - mNextNodep = NULL; -} - - -template -class LLUUIDHashMap -{ -public: - // basic constructor including sorter - LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data), - const DATA_TYPE &null_data); - ~LLUUIDHashMap(); - - inline DATA_TYPE &get(const LLUUID &uuid); - inline BOOL check(const LLUUID &uuid) const; - inline DATA_TYPE &set(const LLUUID &uuid, const DATA_TYPE &type); - inline BOOL remove(const LLUUID &uuid); - void removeAll(); - - inline S32 getLength() const; // Warning, NOT O(1!) -public: - BOOL (*mEquals)(const LLUUID &uuid, const DATA_TYPE &data); - LLUUIDHashNode mNodes[256]; - - S32 mIterCount; -protected: - DATA_TYPE mNull; -}; - - -// -// LLUUIDHashMap implementation -// - -template -LLUUIDHashMap::LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data), - const DATA_TYPE &null_data) -: mEquals(equals), - mIterCount(0), - mNull(null_data) -{ } - -template -LLUUIDHashMap::~LLUUIDHashMap() -{ - removeAll(); -} - -template -void LLUUIDHashMap::removeAll() -{ - S32 bin; - for (bin = 0; bin < 256; bin++) - { - LLUUIDHashNode* nodep = &mNodes[bin]; - - BOOL first = TRUE; - while (nodep) - { - S32 i; - const S32 count = nodep->mCount; - - // Iterate through all members of this node - for (i = 0; i < count; i++) - { - nodep->mData[i] = mNull; - } - - nodep->mCount = 0; - // Done with all objects in this node, go to the next. - - LLUUIDHashNode* curp = nodep; - nodep = nodep->mNextNodep; - - // Delete the node if it's not the first node - if (first) - { - first = FALSE; - curp->mNextNodep = NULL; - } - else - { - delete curp; - } - } - } -} - -template -inline S32 LLUUIDHashMap::getLength() const -{ - S32 count = 0; - S32 bin; - for (bin = 0; bin < 256; bin++) - { - LLUUIDHashNode* nodep = (LLUUIDHashNode*) &mNodes[bin]; - while (nodep) - { - count += nodep->mCount; - nodep = nodep->mNextNodep; - } - } - return count; -} - -template -inline DATA_TYPE &LLUUIDHashMap::get(const LLUUID &uuid) -{ - LLUUIDHashNode* nodep = &mNodes[uuid.mData[0]]; - - // Grab the second byte of the UUID, which is the key for the node data - const S32 second_byte = uuid.mData[1]; - while (nodep) - { - S32 i; - const S32 count = nodep->mCount; - - // Iterate through all members of this node - for (i = 0; i < count; i++) - { - if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i])) - { - // The second byte matched, and our equality test passed. - // We found it. - return nodep->mData[i]; - } - } - - // Done with all objects in this node, go to the next. - nodep = nodep->mNextNodep; - } - return mNull; -} - - -template -inline BOOL LLUUIDHashMap::check(const LLUUID &uuid) const -{ - const LLUUIDHashNode* nodep = &mNodes[uuid.mData[0]]; - - // Grab the second byte of the UUID, which is the key for the node data - const S32 second_byte = uuid.mData[1]; - while (nodep) - { - S32 i; - const S32 count = nodep->mCount; - - // Iterate through all members of this node - for (i = 0; i < count; i++) - { - if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i])) - { - // The second byte matched, and our equality test passed. - // We found it. - return TRUE; - } - } - - // Done with all objects in this node, go to the next. - nodep = nodep->mNextNodep; - } - - // Didn't find anything - return FALSE; -} - - -template -inline DATA_TYPE &LLUUIDHashMap::set(const LLUUID &uuid, const DATA_TYPE &data) -{ - // Set is just like a normal find, except that if we find a match - // we replace it with the input value. - // If we don't find a match, we append to the end of the list. - - LLUUIDHashNode* nodep = &mNodes[uuid.mData[0]]; - - const S32 second_byte = uuid.mData[1]; - while (1) - { - const S32 count = nodep->mCount; - - S32 i; - for (i = 0; i < count; i++) - { - if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i])) - { - // We found a match for this key, replace the data with - // the incoming data. - nodep->mData[i] = data; - return nodep->mData[i]; - } - } - if (!nodep->mNextNodep) - { - // We've iterated through all of the keys without finding a match - if (i < SIZE) - { - // There's still some space on this node, append - // the key and data to it. - nodep->mKey[i] = second_byte; - nodep->mData[i] = data; - nodep->mCount++; - - return nodep->mData[i]; - } - else - { - // This node is full, append a new node to the end. - nodep->mNextNodep = new LLUUIDHashNode; - nodep->mNextNodep->mKey[0] = second_byte; - nodep->mNextNodep->mData[0] = data; - nodep->mNextNodep->mCount = 1; - - return nodep->mNextNodep->mData[0]; - } - } - - // No match on this node, go to the next - nodep = nodep->mNextNodep; - } -} - - -template -inline BOOL LLUUIDHashMap::remove(const LLUUID &uuid) -{ - if (mIterCount) - { - // We don't allow remove when we're iterating, it's bad karma! - llerrs << "Attempted remove while an outstanding iterator in LLUUIDHashMap!" << llendl; - } - // Remove is the trickiest operation. - // What we want to do is swap the last element of the last - // node if we find the one that we want to remove, but we have - // to deal with deleting the node from the tail if it's empty, but - // NOT if it's the only node left. - - LLUUIDHashNode *nodep = &mNodes[uuid.mData[0]]; - - // Not empty, we need to search through the nodes - const S32 second_byte = uuid.mData[1]; - - // A modification of the standard search algorithm. - while (nodep) - { - const S32 count = nodep->mCount; - - S32 i; - for (i = 0; i < count; i++) - { - if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i])) - { - // We found the node that we want to remove. - // Find the last (and next-to-last) node, and the index of the last - // element. We could conceviably start from the node we're on, - // but that makes it more complicated, this is easier. - - LLUUIDHashNode *prevp = &mNodes[uuid.mData[0]]; - LLUUIDHashNode *lastp = prevp; - - // Find the last and next-to-last - while (lastp->mNextNodep) - { - prevp = lastp; - lastp = lastp->mNextNodep; - } - - // First, swap in the last to the current location. - nodep->mKey[i] = lastp->mKey[lastp->mCount - 1]; - nodep->mData[i] = lastp->mData[lastp->mCount - 1]; - - // Now, we delete the entry - lastp->mCount--; - lastp->mData[lastp->mCount] = mNull; - - if (!lastp->mCount) - { - // We deleted the last element! - if (lastp != &mNodes[uuid.mData[0]]) - { - // Only blitz the node if it's not the head - // Set the previous node to point to NULL, then - // blitz the empty last node - prevp->mNextNodep = NULL; - delete lastp; - } - } - return TRUE; - } - } - - // Iterate to the next node, we've scanned all the entries in this one. - nodep = nodep->mNextNodep; - } - return FALSE; -} - - -// -// LLUUIDHashMapIter -// - -template -class LLUUIDHashMapIter -{ -public: - LLUUIDHashMapIter(LLUUIDHashMap *hash_mapp); - ~LLUUIDHashMapIter(); - - - inline void reset(); - inline void first(); - inline void next(); - inline BOOL done() const; - - DATA_TYPE& operator*() const - { - return mCurHashNodep->mData[mCurHashNodeKey]; - } - DATA_TYPE* operator->() const - { - return &(operator*()); - } - -protected: - LLUUIDHashMap *mHashMapp; - LLUUIDHashNode *mCurHashNodep; - - S32 mCurHashMapNodeNum; - S32 mCurHashNodeKey; - - DATA_TYPE mNull; -}; - - -// -// LLUUIDHashMapIter Implementation -// -template -LLUUIDHashMapIter::LLUUIDHashMapIter(LLUUIDHashMap *hash_mapp) -{ - mHashMapp = hash_mapp; - mCurHashNodep = NULL; - mCurHashMapNodeNum = 0; - mCurHashNodeKey = 0; -} - -template -LLUUIDHashMapIter::~LLUUIDHashMapIter() -{ - reset(); -} - -template -inline void LLUUIDHashMapIter::reset() -{ - if (mCurHashNodep) - { - // We're partway through an iteration, we can clean up now - mHashMapp->mIterCount--; - mCurHashNodep = NULL; - } -} - -template -inline void LLUUIDHashMapIter::first() -{ - // Iterate through until we find the first non-empty node; - S32 i; - for (i = 0; i < 256; i++) - { - if (mHashMapp->mNodes[i].mCount) - { - if (!mCurHashNodep) - { - // Increment, since it's no longer safe for us to do a remove - mHashMapp->mIterCount++; - } - - mCurHashNodep = &mHashMapp->mNodes[i]; - mCurHashMapNodeNum = i; - mCurHashNodeKey = 0; - //return mCurHashNodep->mData[0]; - return; - } - } - - // Completely empty! - mCurHashNodep = NULL; - //return mNull; - return; -} - -template -inline BOOL LLUUIDHashMapIter::done() const -{ - return mCurHashNodep ? FALSE : TRUE; -} - -template -inline void LLUUIDHashMapIter::next() -{ - // No current entry, this iterator is done - if (!mCurHashNodep) - { - //return mNull; - return; - } - - // Go to the next element - mCurHashNodeKey++; - if (mCurHashNodeKey < mCurHashNodep->mCount) - { - // We're not done with this node, return the current element - //return mCurHashNodep->mData[mCurHashNodeKey]; - return; - } - - // Done with this node, move to the next - mCurHashNodep = mCurHashNodep->mNextNodep; - if (mCurHashNodep) - { - // Return the first element - mCurHashNodeKey = 0; - //return mCurHashNodep->mData[0]; - return; - } - - // Find the next non-empty node (keyed on the first byte) - mCurHashMapNodeNum++; - - S32 i; - for (i = mCurHashMapNodeNum; i < 256; i++) - { - if (mHashMapp->mNodes[i].mCount) - { - // We found one that wasn't empty - mCurHashNodep = &mHashMapp->mNodes[i]; - mCurHashMapNodeNum = i; - mCurHashNodeKey = 0; - //return mCurHashNodep->mData[0]; - return; - } - } - - // OK, we're done, nothing else to iterate - mCurHashNodep = NULL; - mHashMapp->mIterCount--; // Decrement since we're safe to do removes now - //return mNull; -} - -#endif // LL_LLUUIDHASHMAP_H diff --git a/indra/llcommon/llversionviewer.h.in b/indra/llcommon/llversionviewer.h.in index 030623505..e1fa1cab3 100644 --- a/indra/llcommon/llversionviewer.h.in +++ b/indra/llcommon/llversionviewer.h.in @@ -34,8 +34,8 @@ #define LL_LLVERSIONVIEWER_H const S32 LL_VERSION_MAJOR = 1; -const S32 LL_VERSION_MINOR = 7; -const S32 LL_VERSION_PATCH = 3; +const S32 LL_VERSION_MINOR = 8; +const S32 LL_VERSION_PATCH = 0; const S32 LL_VERSION_BUILD = ${vBUILD}; const char * const LL_CHANNEL = "${VIEWER_CHANNEL}"; diff --git a/indra/llcommon/sguuidhash.h b/indra/llcommon/sguuidhash.h new file mode 100644 index 000000000..b6b65aa0a --- /dev/null +++ b/indra/llcommon/sguuidhash.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2013 Siana Gearz + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 */ + +#ifndef SGUUIDHASH_H +#define SGUUIDHASH_H + +#include "lluuid.h" +#include +#include + +namespace boost { + template<> class hash { + public: + size_t operator()(const LLUUID& id ) const + { + return *reinterpret_cast(id.mData); + } + }; +} + + +#endif diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index c09361144..e072b5c79 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -390,11 +390,6 @@ bool LLCrashLogger::init() // Start state machine thread. startEngineThread(); - // Start curl thread. - AICurlInterface::startCurlThread(64, // CurlMaxTotalConcurrentConnections - 8, // CurlConcurrentConnectionsPerHost - true); // NoVerifySSLCert - // We assume that all the logs we're looking for reside on the current drive gDirUtilp->initAppDirs("SecondLife"); @@ -414,6 +409,9 @@ bool LLCrashLogger::init() return false; } + // Start curl thread. + AICurlInterface::startCurlThread(&mCrashSettings); + gServicePump = new LLPumpIO; //If we've opened the crash logger, assume we can delete the marker file if it exists diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index 0fcd38064..e3eb2bded 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -41,7 +41,7 @@ namespace expression { //TODO: If we can find a better way to do this with boost::pheonix::bind lets do it -namespace { // anonymous +//namespace { // anonymous template T min_glue(T a, T b) @@ -91,7 +91,7 @@ struct lazy_bfunc_ } }; -} // end namespace anonymous +//} // end namespace anonymous template struct grammar diff --git a/indra/llmath/llmatrix3a.cpp b/indra/llmath/llmatrix3a.cpp index ab077abcb..ad008e9d3 100644 --- a/indra/llmath/llmatrix3a.cpp +++ b/indra/llmath/llmatrix3a.cpp @@ -24,6 +24,7 @@ * $/LicenseInfo$ */ +#include "sys.h" #include "llmath.h" static LL_ALIGN_16(const F32 M_IDENT_3A[12]) = diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index ebee8c548..53e890040 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -22,9 +22,10 @@ include_directories( ) set(llmessage_SOURCE_FILES + aiaverage.cpp aicurl.cpp aicurleasyrequeststatemachine.cpp - aicurlperhost.cpp + aicurlperservice.cpp aicurlthread.cpp aihttpheaders.cpp aihttptimeout.cpp @@ -109,9 +110,10 @@ set(llmessage_SOURCE_FILES set(llmessage_HEADER_FILES CMakeLists.txt + aiaverage.h aicurl.h aicurleasyrequeststatemachine.h - aicurlperhost.h + aicurlperservice.h aicurlprivate.h aicurlthread.h aihttpheaders.h diff --git a/indra/llmessage/aiaverage.cpp b/indra/llmessage/aiaverage.cpp new file mode 100644 index 000000000..10aafd852 --- /dev/null +++ b/indra/llmessage/aiaverage.cpp @@ -0,0 +1,81 @@ +/** + * @file aiaverage.cpp + * @brief Implementation of AIAverage + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 11/04/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "sys.h" +#include "aiaverage.h" +#include "llerror.h" // llassert + +void AIAverage::cleanup(U64 clock_tick) +{ + // This expression can fail because the curl thread caches the time in + // sTime_10ms for the duration of an entire loop. Therefore, the time can + // go into the next 40ms and a texture fetch worker thread might call + // cleanup() with that time, setting mCurrentClock to a value (one) + // larger than sTime_10ms / 4. Next, the curl thread can continue to call + // this function with the smaller value; in that case just add the new + // data to the current bucket. + // + // Or, this is just the one-time initialization that happens the first + // time this is called. In that case initialize just mCurrentClock: + // the rest is already initialized upon construction. + if (LL_LIKELY(clock_tick > mCurrentClock)) + { + // Advance to the next bucket. + ++mCurrentBucket; + mCurrentBucket %= mNrOfBuckets; + // Initialize the new bucket. + mData[mCurrentBucket].time = clock_tick; + // Clean up old buckets. + U64 old_time = clock_tick - mNrOfBuckets; + if (LL_UNLIKELY(mTail == mCurrentBucket) || // Extremely unlikely: only happens when data was added EVERY clock tick for the past mNrOfBuckets clock ticks. + mData[mTail].time <= old_time) + { + do + { + mSum -= mData[mTail].sum; + mN -= mData[mTail].n; + mData[mTail].sum = 0; + mData[mTail].n = 0; + ++mTail; + if (LL_UNLIKELY(mTail == mNrOfBuckets)) + { + mTail = 0; + } + } + while (mData[mTail].time <= old_time); + } + // This was set to zero when mTail passed this point (likely not this call, but a few calls ago). + llassert(mData[mCurrentBucket].sum == 0 && + mData[mCurrentBucket].n == 0); + } + mCurrentClock = clock_tick; + return; +} + diff --git a/indra/llmessage/aiaverage.h b/indra/llmessage/aiaverage.h new file mode 100644 index 000000000..ee588bd9f --- /dev/null +++ b/indra/llmessage/aiaverage.h @@ -0,0 +1,109 @@ +/** + * @file aiaverage.h + * @brief Definition of class AIAverage + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 11/04/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIAVERAGE_H +#define AIAVERAGE_H + +#include "llpreprocessor.h" +#include "stdtypes.h" // U32, U64 +#include "llthread.h" // LLMutex +#include // size_t +#include + +class AIAverage { + private: + struct Data { + U32 sum; // Accumulated sum of the 'n' passed to operator()(size_t n, U64 clock_tick) with clock_tick == time. + U32 n; // The number of calls to operator(). + U64 time; // The clock_tick as passed to operator()(size_t n, U64 clock_tick) that sum corresponds to. + }; + + U64 mCurrentClock; // The current (last) time that operator() was called with, or -1 when not initialized. + int mTail; // The oldest bucket with still valid data. + int mCurrentBucket; // The bucket that corresponds to mCurrentClock. + size_t mSum; // The sum of all the 'n' passed to operator()(size_t n, U64 clock_tick) for all passed mNrOfBuckets time units. + U32 mN; // The number of calls to operator(). + int const mNrOfBuckets; // Size of mData. + std::vector mData; // The buckets. + + mutable LLMutex mLock; // Mutex for all of the above data. + + public: + AIAverage(int number_of_buckets) : mCurrentClock(~(U64)0), mTail(0), mCurrentBucket(0), mSum(0), mN(0), mNrOfBuckets(number_of_buckets), mData(number_of_buckets) + { + // Fill mData with all zeroes (much faster than adding a constructor to Data). + std::memset(&*mData.begin(), 0, number_of_buckets * sizeof(Data)); + } + size_t addData(U32 n, U64 clock_tick) + { + DoutEntering(dc::curl, "AIAverage::addData(" << n << ", " << clock_tick << ")"); + mLock.lock(); + if (LL_UNLIKELY(clock_tick != mCurrentClock)) + { + cleanup(clock_tick); + } + mSum += n; + mN += 1; + mData[mCurrentBucket].sum += n; + mData[mCurrentBucket].n += 1; + size_t sum = mSum; + mLock.unlock(); + Dout(dc::curl, "Current sum: " << sum << ", average: " << (sum / mN)); + return sum; + } + size_t truncateData(U64 clock_tick) + { + mLock.lock(); + if (clock_tick != mCurrentClock) + { + cleanup(clock_tick); + } + size_t sum = mSum; + mLock.unlock(); + return sum; + } + double getAverage(double avg_no_data) const + { + mLock.lock(); + double avg = mSum; + llassert(mN != 0 || mSum == 0); + if (LL_UNLIKELY(mN == 0)) + avg = avg_no_data; + else + avg /= mN; + mLock.unlock(); + return avg; + } + + private: + void cleanup(U64 clock_tick); +}; + +#endif // AIAVERAGE diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 3e3e510e1..a9c15c5c4 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -58,7 +58,7 @@ #include "aihttpheaders.h" #include "aihttptimeoutpolicy.h" #include "aicurleasyrequeststatemachine.h" -#include "aicurlperhost.h" +#include "aicurlperservice.h" //================================================================================== // Debug Settings @@ -298,6 +298,7 @@ LLAtomicU32 Stats::easy_init_errors; LLAtomicU32 Stats::easy_cleanup_calls; LLAtomicU32 Stats::multi_calls; LLAtomicU32 Stats::multi_errors; +LLAtomicU32 Stats::running_handles; LLAtomicU32 Stats::AICurlEasyRequest_count; LLAtomicU32 Stats::AICurlEasyRequestStateMachine_count; LLAtomicU32 Stats::BufferedCurlEasyRequest_count; @@ -460,6 +461,12 @@ void setCAPath(std::string const& path) CertificateAuthority_w->path = path; } +// THREAD-SAFE +U32 getNumHTTPRunning(void) +{ + return Stats::running_handles; +} + //static void Stats::print(void) { @@ -952,9 +959,9 @@ CurlEasyRequest::~CurlEasyRequest() // be available anymore. send_handle_events_to(NULL); revokeCallbacks(); - if (mPerHostPtr) + if (mPerServicePtr) { - PerHostRequestQueue::release(mPerHostPtr); + AIPerService::release(mPerServicePtr); } // This wasn't freed yet if the request never finished. curl_slist_free_all(mHeaders); @@ -1084,56 +1091,6 @@ void CurlEasyRequest::applyDefaultOptions(void) ); } -// url must be of the form -// (see http://www.ietf.org/rfc/rfc3986.txt Appendix A for definitions not given here): -// -// url = sheme ":" hier-part [ "?" query ] [ "#" fragment ] -// hier-part = "//" authority path-abempty -// authority = [ userinfo "@" ] host [ ":" port ] -// path-abempty = *( "/" segment ) -// -// That is, a hier-part of the form '/ path-absolute', '/ path-rootless' or -// '/ path-empty' is NOT allowed here. This should be safe because we only -// call this function for curl access, any file access would use APR. -// -// However, as a special exception, this function allows: -// -// url = authority path-abempty -// -// without the 'sheme ":" "//"' parts. -// -// As follows from the ABNF (see RFC, Appendix A): -// - authority is either terminated by a '/' or by the end of the string because -// neither userinfo, host nor port may contain a '/'. -// - userinfo does not contain a '@', and if it exists, is always terminated by a '@'. -// - port does not contain a ':', and if it exists is always prepended by a ':'. -// -// Only called by CurlEasyRequest::finalizeRequest. -static std::string extract_canonical_hostname(std::string const& url) -{ - std::string::size_type pos; - std::string::size_type authority = 0; // Default if there is no sheme. - if ((pos = url.find("://")) != url.npos && pos < url.find('/')) authority = pos + 3; // Skip the "sheme://" if any, the second find is to avoid finding a "://" as part of path-abempty. - std::string::size_type host = authority; // Default if there is no userinfo. - if ((pos = url.find('@', authority)) != url.npos) host = pos + 1; // Skip the "userinfo@" if any. - authority = url.length() - 1; // Default last character of host if there is no path-abempty. - if ((pos = url.find('/', host)) != url.npos) authority = pos - 1; // Point to last character of host. - std::string::size_type len = url.find_last_not_of(":0123456789", authority) - host + 1; // Skip trailing ":port", if any. - std::string hostname(url, host, len); -#if APR_CHARSET_EBCDIC -#error Not implemented -#else - // Convert hostname to lowercase in a way that we compare two hostnames equal iff libcurl does. - for (std::string::iterator iter = hostname.begin(); iter != hostname.end(); ++iter) - { - int c = *iter; - if (c >= 'A' && c <= 'Z') - *iter = c + ('a' - 'A'); - } -#endif - return hostname; -} - void CurlEasyRequest::finalizeRequest(std::string const& url, AIHTTPTimeoutPolicy const& policy, AICurlEasyRequestStateMachine* state_machine) { DoutCurlEntering("CurlEasyRequest::finalizeRequest(\"" << url << "\", " << policy.name() << ", " << (void*)state_machine << ")"); @@ -1156,8 +1113,8 @@ void CurlEasyRequest::finalizeRequest(std::string const& url, AIHTTPTimeoutPolic #endif setopt(CURLOPT_HTTPHEADER, mHeaders); setoptString(CURLOPT_URL, url); - llassert(!mPerHostPtr); - mLowercaseHostname = extract_canonical_hostname(url); + llassert(!mPerServicePtr); + mLowercaseServicename = AIPerService::extract_canonical_servicename(url); mTimeoutPolicy = &policy; state_machine->setTotalDelayTimeout(policy.getTotalDelay()); // The following line is a bit tricky: we store a pointer to the object without increasing its reference count. @@ -1183,7 +1140,7 @@ void CurlEasyRequest::finalizeRequest(std::string const& url, AIHTTPTimeoutPolic // // get less connect time, while it still (also) has to wait for this DNS lookup. void CurlEasyRequest::set_timeout_opts(void) { - setopt(CURLOPT_CONNECTTIMEOUT, mTimeoutPolicy->getConnectTimeout(mLowercaseHostname)); + setopt(CURLOPT_CONNECTTIMEOUT, mTimeoutPolicy->getConnectTimeout(getLowercaseHostname())); setopt(CURLOPT_TIMEOUT, mTimeoutPolicy->getCurlTransaction()); } @@ -1279,22 +1236,27 @@ void CurlEasyRequest::queued_for_removal(AICurlEasyRequest_wat& curl_easy_reques } #endif -PerHostRequestQueuePtr CurlEasyRequest::getPerHostPtr(void) +AIPerServicePtr CurlEasyRequest::getPerServicePtr(void) { - if (!mPerHostPtr) + if (!mPerServicePtr) { - // mPerHostPtr is really just a speed-up cache. - // The reason we can cache it is because mLowercaseHostname is only set + // mPerServicePtr is really just a speed-up cache. + // The reason we can cache it is because mLowercaseServicename is only set // in finalizeRequest which may only be called once: it never changes. - mPerHostPtr = PerHostRequestQueue::instance(mLowercaseHostname); + mPerServicePtr = AIPerService::instance(mLowercaseServicename); } - return mPerHostPtr; + return mPerServicePtr; } -bool CurlEasyRequest::removeFromPerHostQueue(AICurlEasyRequest const& easy_request) const +bool CurlEasyRequest::removeFromPerServiceQueue(AICurlEasyRequest const& easy_request) const { // Note that easy_request (must) represent(s) this object; it's just passed for convenience. - return mPerHostPtr && PerHostRequestQueue_wat(*mPerHostPtr)->cancel(easy_request); + return mPerServicePtr && PerService_wat(*mPerServicePtr)->cancel(easy_request); +} + +std::string CurlEasyRequest::getLowercaseHostname(void) const +{ + return mLowercaseServicename.substr(0, mLowercaseServicename.find_last_of(':')); } //----------------------------------------------------------------------------- @@ -1305,8 +1267,10 @@ static int const HTTP_REDIRECTS_DEFAULT = 10; LLChannelDescriptors const BufferedCurlEasyRequest::sChannels; LLMutex BufferedCurlEasyRequest::sResponderCallbackMutex; bool BufferedCurlEasyRequest::sShuttingDown = false; +AIAverage BufferedCurlEasyRequest::sHTTPBandwidth(25); -BufferedCurlEasyRequest::BufferedCurlEasyRequest() : mRequestTransferedBytes(0), mResponseTransferedBytes(0), mBufferEventsTarget(NULL), mStatus(HTTP_INTERNAL_ERROR_OTHER) +BufferedCurlEasyRequest::BufferedCurlEasyRequest() : + mRequestTransferedBytes(0), mTotalRawBytes(0), mStatus(HTTP_INTERNAL_ERROR_OTHER), mBufferEventsTarget(NULL), mQueueIfTooMuchBandwidthUsage(false) { AICurlInterface::Stats::BufferedCurlEasyRequest_count++; } @@ -1370,7 +1334,7 @@ void BufferedCurlEasyRequest::resetState(void) mOutput.reset(); mInput.reset(); mRequestTransferedBytes = 0; - mResponseTransferedBytes = 0; + mTotalRawBytes = 0; mBufferEventsTarget = NULL; mStatus = HTTP_INTERNAL_ERROR_OTHER; } diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index 9244839b7..d15c72cd8 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -52,6 +52,7 @@ #include "stdtypes.h" // U16, S32, U32, F64 #include "llatomic.h" // LLAtomicU32 #include "aithreadsafe.h" +#include "aicurlperservice.h" // AIPerServicePtr // Debug Settings. extern bool gNoVerifySSLCert; @@ -60,6 +61,7 @@ class LLSD; class LLBufferArray; class LLChannelDescriptors; class AIHTTPTimeoutPolicy; +class LLControlGroup; // Some pretty printing for curl easy handle related things: // Print the lock object related to the current easy handle in every debug output. @@ -133,6 +135,7 @@ struct Stats { static LLAtomicU32 easy_cleanup_calls; static LLAtomicU32 multi_calls; static LLAtomicU32 multi_errors; + static LLAtomicU32 running_handles; static LLAtomicU32 AICurlEasyRequest_count; static LLAtomicU32 AICurlEasyRequestStateMachine_count; static LLAtomicU32 BufferedCurlEasyRequest_count; @@ -153,7 +156,7 @@ struct Stats { // Called to handle changes in Debug Settings. bool handleCurlMaxTotalConcurrentConnections(LLSD const& newvalue); -bool handleCurlConcurrentConnectionsPerHost(LLSD const& newvalue); +bool handleCurlConcurrentConnectionsPerService(LLSD const& newvalue); bool handleNoVerifySSLCert(LLSD const& newvalue); // Called once at start of application (from newview/llappviewer.cpp by main thread (before threads are created)), @@ -161,7 +164,7 @@ bool handleNoVerifySSLCert(LLSD const& newvalue); void initCurl(void); // Called once at start of application (from LLAppViewer::initThreads), starts AICurlThread. -void startCurlThread(U32 CurlMaxTotalConcurrentConnections, U32 CurlConcurrentConnectionsPerHost, bool NoVerifySSLCert); +void startCurlThread(LLControlGroup* control_group); // Called once at the end of application before terminating other threads (most notably the texture thread workers) // with the purpose to stop the curl thread from doing any call backs to running responders: the responders sometimes @@ -185,6 +188,22 @@ void setCAFile(std::string const& file); // Can be used to set the path to the Certificate Authority file. void setCAPath(std::string const& file); +// Returns number of queued 'add' commands minus the number of queued 'remove' commands. +U32 getNumHTTPCommands(void); + +// Returns the number of queued requests. +U32 getNumHTTPQueued(void); + +// Returns the number of curl requests currently added to the multi handle. +U32 getNumHTTPAdded(void); + +// This used to be LLAppViewer::getTextureFetch()->getNumHTTPRequests(). +// Returns the number of active curl easy handles (that are actually attempting to download something). +U32 getNumHTTPRunning(void); + +// Cache for gSavedSettings so we have access from llmessage. +extern LLControlGroup* sConfigGroup; + } // namespace AICurlInterface // Forward declaration (see aicurlprivate.h). diff --git a/indra/llmessage/aicurlperhost.cpp b/indra/llmessage/aicurlperhost.cpp deleted file mode 100644 index b444ba6df..000000000 --- a/indra/llmessage/aicurlperhost.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/** - * @file aiperhost.cpp - * @brief Implementation of PerHostRequestQueue - * - * Copyright (c) 2012, Aleric Inglewood. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution. - * - * CHANGELOG - * and additional copyright holders. - * - * 04/11/2012 - * Initial version, written by Aleric Inglewood @ SL - */ - -#include "sys.h" -#include "aicurlperhost.h" -#include "aicurlthread.h" - -#undef AICurlPrivate - -namespace AICurlPrivate { - -PerHostRequestQueue::threadsafe_instance_map_type PerHostRequestQueue::sInstanceMap; -U32 curl_concurrent_connections_per_host; - -//static -PerHostRequestQueuePtr PerHostRequestQueue::instance(std::string const& hostname) -{ - llassert(!hostname.empty()); - instance_map_wat instance_map_w(sInstanceMap); - PerHostRequestQueue::iterator iter = instance_map_w->find(hostname); - if (iter == instance_map_w->end()) - { - iter = instance_map_w->insert(instance_map_type::value_type(hostname, new RefCountedThreadSafePerHostRequestQueue)).first; - } - // Note: the creation of PerHostRequestQueuePtr MUST be protected by the lock on sInstanceMap (see release()). - return iter->second; -} - -//static -void PerHostRequestQueue::release(PerHostRequestQueuePtr& instance) -{ - if (instance->exactly_two_left()) // Being 'instance' and the one in sInstanceMap. - { - // The viewer can be have left main() we can't access the global sInstanceMap anymore. - if (LLApp::isStopped()) - { - return; - } - instance_map_wat instance_map_w(sInstanceMap); - // It is possible that 'exactly_two_left' is not up to date anymore. - // Therefore, recheck the condition now that we have locked sInstanceMap. - if (!instance->exactly_two_left()) - { - // Some other thread added this host in the meantime. - return; - } - // The reference in the map is the last one; that means there can't be any curl easy requests queued for this host. - llassert(PerHostRequestQueue_wat(*instance)->mQueuedRequests.empty()); - // Find the host and erase it from the map. - iterator const end = instance_map_w->end(); - for(iterator iter = instance_map_w->begin(); iter != end; ++iter) - { - if (instance == iter->second) - { - instance_map_w->erase(iter); - instance.reset(); - return; - } - } - // We should always find the host. - llassert(false); - } - instance.reset(); -} - -bool PerHostRequestQueue::throttled() const -{ - llassert(mAdded <= int(curl_concurrent_connections_per_host)); - return mAdded == int(curl_concurrent_connections_per_host); -} - -void PerHostRequestQueue::added_to_multi_handle(void) -{ - llassert(mAdded < int(curl_concurrent_connections_per_host)); - ++mAdded; -} - -void PerHostRequestQueue::removed_from_multi_handle(void) -{ - --mAdded; - llassert(mAdded >= 0); -} - -void PerHostRequestQueue::queue(AICurlEasyRequest const& easy_request) -{ - mQueuedRequests.push_back(easy_request.get_ptr()); -} - -bool PerHostRequestQueue::cancel(AICurlEasyRequest const& easy_request) -{ - queued_request_type::iterator const end = mQueuedRequests.end(); - queued_request_type::iterator cur = std::find(mQueuedRequests.begin(), end, easy_request.get_ptr()); - - if (cur == end) - return false; // Not found. - - // We can't use erase because that uses assignment to move elements, - // because it isn't thread-safe. Therefore, move the element that we found to - // the back with swap (could just swap with the end immediately, but I don't - // want to break the order in which requests where added). Swap is also not - // thread-safe, but OK here because it only touches the objects in the deque, - // and the deque is protected by the lock on the PerHostRequestQueue object. - queued_request_type::iterator prev = cur; - while (++cur != end) - { - prev->swap(*cur); // This is safe, - prev = cur; - } - mQueuedRequests.pop_back(); // if this is safe. - return true; -} - -void PerHostRequestQueue::add_queued_to(curlthread::MultiHandle* multi_handle) -{ - if (!mQueuedRequests.empty()) - { - multi_handle->add_easy_request(mQueuedRequests.front()); - mQueuedRequests.pop_front(); - } -} - -//static -void PerHostRequestQueue::purge(void) -{ - instance_map_wat instance_map_w(sInstanceMap); - for (iterator host = instance_map_w->begin(); host != instance_map_w->end(); ++host) - { - Dout(dc::curl, "Purging queue of host \"" << host->first << "\"."); - PerHostRequestQueue_wat(*host->second)->mQueuedRequests.clear(); - } -} - -// Friend functions of RefCountedThreadSafePerHostRequestQueue - -void intrusive_ptr_add_ref(RefCountedThreadSafePerHostRequestQueue* per_host) -{ - per_host->mReferenceCount++; -} - -void intrusive_ptr_release(RefCountedThreadSafePerHostRequestQueue* per_host) -{ - if (--per_host->mReferenceCount == 0) - { - delete per_host; - } -} - -} // namespace AICurlPrivate diff --git a/indra/llmessage/aicurlperhost.h b/indra/llmessage/aicurlperhost.h deleted file mode 100644 index c464d490c..000000000 --- a/indra/llmessage/aicurlperhost.h +++ /dev/null @@ -1,135 +0,0 @@ -/** - * @file aicurlperhost.h - * @brief Definition of class PerHostRequestQueue - * - * Copyright (c) 2012, Aleric Inglewood. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution. - * - * CHANGELOG - * and additional copyright holders. - * - * 04/11/2012 - * Initial version, written by Aleric Inglewood @ SL - */ - -#ifndef AICURLPERHOST_H -#define AICURLPERHOST_H - -#include "llerror.h" // llassert -#include -#include -#include -#include -#include "aithreadsafe.h" - -class AICurlEasyRequest; - -namespace AICurlPrivate { -namespace curlthread { class MultiHandle; } - -class PerHostRequestQueue; -class RefCountedThreadSafePerHostRequestQueue; -class ThreadSafeBufferedCurlEasyRequest; - -// Forward declaration of BufferedCurlEasyRequestPtr (see aicurlprivate.h). -typedef boost::intrusive_ptr BufferedCurlEasyRequestPtr; - -// PerHostRequestQueue objects are created by the curl thread and destructed by the main thread. -// We need locking. -typedef AIThreadSafeSimpleDC threadsafe_PerHostRequestQueue; -typedef AIAccessConst PerHostRequestQueue_crat; -typedef AIAccess PerHostRequestQueue_rat; -typedef AIAccess PerHostRequestQueue_wat; - -// We can't put threadsafe_PerHostRequestQueue in a std::map because you can't copy a mutex. -// Therefore, use an intrusive pointer for the threadsafe type. -typedef boost::intrusive_ptr PerHostRequestQueuePtr; - -//----------------------------------------------------------------------------- -// PerHostRequestQueue - -// This class provides a static interface to create and maintain instances -// of PerHostRequestQueue objects, so that at any moment there is at most -// one instance per hostname. Those instances then are used to queue curl -// requests when the maximum number of connections for that host already -// have been reached. -class PerHostRequestQueue { - private: - typedef std::map instance_map_type; - typedef AIThreadSafeSimpleDC threadsafe_instance_map_type; - typedef AIAccess instance_map_rat; - typedef AIAccess instance_map_wat; - - static threadsafe_instance_map_type sInstanceMap; // Map of PerHostRequestQueue instances with the hostname as key. - - friend class AIThreadSafeSimpleDC; //threadsafe_PerHostRequestQueue - PerHostRequestQueue(void) : mAdded(0) { } - - public: - typedef instance_map_type::iterator iterator; - typedef instance_map_type::const_iterator const_iterator; - - // Return (possibly create) a unique instance for the given hostname. - static PerHostRequestQueuePtr instance(std::string const& hostname); - - // Release instance (object will be deleted if this was the last instance). - static void release(PerHostRequestQueuePtr& instance); - - // Remove everything. Called upon viewer exit. - static void purge(void); - - private: - typedef std::deque queued_request_type; - - int mAdded; // Number of active easy handles with this host. - queued_request_type mQueuedRequests; // Waiting (throttled) requests. - - public: - void added_to_multi_handle(void); // Called when an easy handle for this host has been added to the multi handle. - void removed_from_multi_handle(void); // Called when an easy handle for this host is removed again from the multi handle. - bool throttled(void) const; // Returns true if the maximum number of allowed requests for this host have been added to the multi handle. - - void queue(AICurlEasyRequest const& easy_request); // Add easy_request to the queue. - bool cancel(AICurlEasyRequest const& easy_request); // Remove easy_request from the queue (if it's there). - - void add_queued_to(curlthread::MultiHandle* mh); // Add queued easy handle (if any) to the multi handle. The request is removed from the queue, - // followed by either a call to added_to_multi_handle() or to queue() to add it back. - private: - // Disallow copying. - PerHostRequestQueue(PerHostRequestQueue const&) { } -}; - -class RefCountedThreadSafePerHostRequestQueue : public threadsafe_PerHostRequestQueue { - public: - RefCountedThreadSafePerHostRequestQueue(void) : mReferenceCount(0) { } - bool exactly_two_left(void) const { return mReferenceCount == 2; } - - private: - // Used by PerHostRequestQueuePtr. Object is deleted when reference count reaches zero. - LLAtomicU32 mReferenceCount; - - friend void intrusive_ptr_add_ref(RefCountedThreadSafePerHostRequestQueue* p); - friend void intrusive_ptr_release(RefCountedThreadSafePerHostRequestQueue* p); -}; - -extern U32 curl_concurrent_connections_per_host; - -} // namespace AICurlPrivate - -#endif // AICURLPERHOST_H diff --git a/indra/llmessage/aicurlperservice.cpp b/indra/llmessage/aicurlperservice.cpp new file mode 100644 index 000000000..8f8573ea6 --- /dev/null +++ b/indra/llmessage/aicurlperservice.cpp @@ -0,0 +1,368 @@ +/** + * @file aiperservice.cpp + * @brief Implementation of AIPerService + * + * Copyright (c) 2012, 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 04/11/2012 + * Initial version, written by Aleric Inglewood @ SL + * + * 06/04/2013 + * Renamed AICurlPrivate::PerHostRequestQueue[Ptr] to AIPerHostRequestQueue[Ptr] + * to allow public access. + * + * 09/04/2013 + * Renamed everything "host" to "service" and use "hostname:port" as key + * instead of just "hostname". + */ + +#include "sys.h" +#include "aicurlperservice.h" +#include "aicurlthread.h" +#include "llcontrol.h" + +AIPerService::threadsafe_instance_map_type AIPerService::sInstanceMap; +AIThreadSafeSimpleDC AIPerService::sTotalQueued; + +#undef AICurlPrivate + +namespace AICurlPrivate { + +// Cached value of CurlConcurrentConnectionsPerService. +U32 CurlConcurrentConnectionsPerService; + +// Friend functions of RefCountedThreadSafePerService + +void intrusive_ptr_add_ref(RefCountedThreadSafePerService* per_service) +{ + per_service->mReferenceCount++; +} + +void intrusive_ptr_release(RefCountedThreadSafePerService* per_service) +{ + if (--per_service->mReferenceCount == 0) + { + delete per_service; + } +} + +} // namespace AICurlPrivate + +using namespace AICurlPrivate; + +AIPerService::AIPerService(void) : + mApprovedRequests(0), mQueuedCommands(0), mAdded(0), mQueueEmpty(false), + mQueueFull(false), mRequestStarvation(false), mHTTPBandwidth(25), // 25 = 1000 ms / 40 ms. + mConcurrectConnections(CurlConcurrentConnectionsPerService), + mMaxPipelinedRequests(CurlConcurrentConnectionsPerService) +{ +} + +AIPerService::~AIPerService() +{ +} + +// Fake copy constructor. +AIPerService::AIPerService(AIPerService const&) : mHTTPBandwidth(0) +{ +} + +// url must be of the form +// (see http://www.ietf.org/rfc/rfc3986.txt Appendix A for definitions not given here): +// +// url = sheme ":" hier-part [ "?" query ] [ "#" fragment ] +// hier-part = "//" authority path-abempty +// authority = [ userinfo "@" ] host [ ":" port ] +// path-abempty = *( "/" segment ) +// +// That is, a hier-part of the form '/ path-absolute', '/ path-rootless' or +// '/ path-empty' is NOT allowed here. This should be safe because we only +// call this function for curl access, any file access would use APR. +// +// However, as a special exception, this function allows: +// +// url = authority path-abempty +// +// without the 'sheme ":" "//"' parts. +// +// As follows from the ABNF (see RFC, Appendix A): +// - authority is either terminated by a '/' or by the end of the string because +// neither userinfo, host nor port may contain a '/'. +// - userinfo does not contain a '@', and if it exists, is always terminated by a '@'. +// - port does not contain a ':', and if it exists is always prepended by a ':'. +// +//static +std::string AIPerService::extract_canonical_servicename(std::string const& url) +{ + char const* p = url.data(); + char const* const end = p + url.size(); + char const* sheme_colon = NULL; + char const* sheme_slash = NULL; + char const* first_ampersand = NULL; + char const* port_colon = NULL; + std::string servicename; + char const* hostname = p; // Default in the case there is no "sheme://userinfo@". + while (p < end) + { + int c = *p; + if (c == ':') + { + if (!port_colon && LLStringOps::isDigit(p[1])) + { + port_colon = p; + } + else if (!sheme_colon && !sheme_slash && !first_ampersand && !port_colon) + { + // Found a colon before any slash or ampersand: this has to be the colon between the sheme and the hier-part. + sheme_colon = p; + } + } + else if (c == '/') + { + if (!sheme_slash && sheme_colon && sheme_colon == p - 1 && !first_ampersand && p[1] == '/') + { + // Found the first '/' in the first occurance of the sequence "://". + sheme_slash = p; + hostname = ++p + 1; // Point hostname to the start of the authority, the default when there is no "userinfo@" part. + servicename.clear(); // Remove the sheme. + } + else + { + // Found slash that is not part of the "sheme://" string. Signals end of authority. + // We're done. + break; + } + } + else if (c == '@') + { + if (!first_ampersand) + { + first_ampersand = p; + hostname = p + 1; + servicename.clear(); // Remove the "userinfo@" + } + } + if (p >= hostname) + { + // Convert hostname to lowercase in a way that we compare two hostnames equal iff libcurl does. +#if APR_CHARSET_EBCDIC +#error Not implemented +#else + if (c >= 'A' && c <= 'Z') + c += ('a' - 'A'); +#endif + servicename += c; + } + ++p; + } + // Strip of any trailing ":80". + if (p - 3 == port_colon && p[-1] == '0' && p[-2] == '8') + { + return servicename.substr(0, p - hostname - 3); + } + return servicename; +} + +//static +AIPerServicePtr AIPerService::instance(std::string const& servicename) +{ + llassert(!servicename.empty()); + instance_map_wat instance_map_w(sInstanceMap); + AIPerService::iterator iter = instance_map_w->find(servicename); + if (iter == instance_map_w->end()) + { + iter = instance_map_w->insert(instance_map_type::value_type(servicename, new RefCountedThreadSafePerService)).first; + } + // Note: the creation of AIPerServicePtr MUST be protected by the lock on sInstanceMap (see release()). + return iter->second; +} + +//static +void AIPerService::release(AIPerServicePtr& instance) +{ + if (instance->exactly_two_left()) // Being 'instance' and the one in sInstanceMap. + { + // The viewer can be have left main() we can't access the global sInstanceMap anymore. + if (LLApp::isStopped()) + { + return; + } + instance_map_wat instance_map_w(sInstanceMap); + // It is possible that 'exactly_two_left' is not up to date anymore. + // Therefore, recheck the condition now that we have locked sInstanceMap. + if (!instance->exactly_two_left()) + { + // Some other thread added this host in the meantime. + return; + } + // The reference in the map is the last one; that means there can't be any curl easy requests queued for this host. + llassert(PerService_rat(*instance)->mQueuedRequests.empty()); + // Find the host and erase it from the map. + iterator const end = instance_map_w->end(); + for(iterator iter = instance_map_w->begin(); iter != end; ++iter) + { + if (instance == iter->second) + { + instance_map_w->erase(iter); + instance.reset(); + return; + } + } + // We should always find the host. + llassert(false); + } + instance.reset(); +} + +bool AIPerService::throttled() const +{ + return mAdded >= mConcurrectConnections; +} + +void AIPerService::added_to_multi_handle(void) +{ + ++mAdded; +} + +void AIPerService::removed_from_multi_handle(void) +{ + --mAdded; + llassert(mAdded >= 0); +} + +void AIPerService::queue(AICurlEasyRequest const& easy_request) +{ + mQueuedRequests.push_back(easy_request.get_ptr()); + TotalQueued_wat(sTotalQueued)->count++; +} + +bool AIPerService::cancel(AICurlEasyRequest const& easy_request) +{ + queued_request_type::iterator const end = mQueuedRequests.end(); + queued_request_type::iterator cur = std::find(mQueuedRequests.begin(), end, easy_request.get_ptr()); + + if (cur == end) + return false; // Not found. + + // We can't use erase because that uses assignment to move elements, + // because it isn't thread-safe. Therefore, move the element that we found to + // the back with swap (could just swap with the end immediately, but I don't + // want to break the order in which requests where added). Swap is also not + // thread-safe, but OK here because it only touches the objects in the deque, + // and the deque is protected by the lock on the AIPerService object. + queued_request_type::iterator prev = cur; + while (++cur != end) + { + prev->swap(*cur); // This is safe, + prev = cur; + } + mQueuedRequests.pop_back(); // if this is safe. + TotalQueued_wat total_queued_w(sTotalQueued); + total_queued_w->count--; + llassert(total_queued_w->count >= 0); + return true; +} + +void AIPerService::add_queued_to(curlthread::MultiHandle* multi_handle) +{ + if (!mQueuedRequests.empty()) + { + multi_handle->add_easy_request(mQueuedRequests.front()); + mQueuedRequests.pop_front(); + if (mQueuedRequests.empty()) + { + // We obtained a request from the queue, and after that there we no more request in the queue of this host. + mQueueEmpty = true; + } + else + { + // We obtained a request from the queue, and even after that there was at least one more request in the queue of this host. + mQueueFull = true; + } + TotalQueued_wat total_queued_w(sTotalQueued); + llassert(total_queued_w->count > 0); + if (!--(total_queued_w->count)) + { + // We obtained a request from the queue, and after that there we no more request in any queue. + total_queued_w->empty = true; + } + else + { + // We obtained a request from the queue, and even after that there was at least one more request in some queue. + total_queued_w->full = true; + } + } + else + { + // We can add a new request, but there is none in the queue! + mRequestStarvation = true; + TotalQueued_wat total_queued_w(sTotalQueued); + if (total_queued_w->count == 0) + { + // The queue of every host is empty! + total_queued_w->starvation = true; + } + } +} + +//static +void AIPerService::purge(void) +{ + instance_map_wat instance_map_w(sInstanceMap); + for (iterator host = instance_map_w->begin(); host != instance_map_w->end(); ++host) + { + Dout(dc::curl, "Purging queue of host \"" << host->first << "\"."); + PerService_wat per_service_w(*host->second); + size_t s = per_service_w->mQueuedRequests.size(); + per_service_w->mQueuedRequests.clear(); + TotalQueued_wat total_queued_w(sTotalQueued); + total_queued_w->count -= s; + llassert(total_queued_w->count >= 0); + } +} + +//static +void AIPerService::adjust_concurrent_connections(int increment) +{ + instance_map_wat instance_map_w(sInstanceMap); + for (AIPerService::iterator iter = instance_map_w->begin(); iter != instance_map_w->end(); ++iter) + { + PerService_wat per_service_w(*iter->second); + U32 old_concurrent_connections = per_service_w->mConcurrectConnections; + per_service_w->mConcurrectConnections = llclamp(old_concurrent_connections + increment, (U32)1, CurlConcurrentConnectionsPerService); + increment = per_service_w->mConcurrectConnections - old_concurrent_connections; + per_service_w->mMaxPipelinedRequests = llmax(per_service_w->mMaxPipelinedRequests + increment, 0); + } +} + +void AIPerService::Approvement::honored(void) +{ + if (!mHonored) + { + mHonored = true; + AICurlPrivate::PerService_wat per_service_w(*mPerServicePtr); + llassert(per_service_w->mApprovedRequests > 0); + per_service_w->mApprovedRequests--; + } +} + diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h new file mode 100644 index 000000000..eab41ad80 --- /dev/null +++ b/indra/llmessage/aicurlperservice.h @@ -0,0 +1,251 @@ +/** + * @file aicurlperservice.h + * @brief Definition of class AIPerService + * + * Copyright (c) 2012, 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 04/11/2012 + * Initial version, written by Aleric Inglewood @ SL + * + * 06/04/2013 + * Renamed AIPrivate::PerHostRequestQueue[Ptr] to AIPerHostRequestQueue[Ptr] + * to allow public access. + * + * 09/04/2013 + * Renamed everything "host" to "service" and use "hostname:port" as key + * instead of just "hostname". + */ + +#ifndef AICURLPERSERVICE_H +#define AICURLPERSERVICE_H + +#include "llerror.h" // llassert +#include +#include +#include +#include +#include "aithreadsafe.h" +#include "aiaverage.h" + +class AICurlEasyRequest; +class AIPerService; + +namespace AICurlPrivate { +namespace curlthread { class MultiHandle; } + +class RefCountedThreadSafePerService; +class ThreadSafeBufferedCurlEasyRequest; + +// Forward declaration of BufferedCurlEasyRequestPtr (see aicurlprivate.h). +typedef boost::intrusive_ptr BufferedCurlEasyRequestPtr; + +// AIPerService objects are created by the curl thread and destructed by the main thread. +// We need locking. +typedef AIThreadSafeSimpleDC threadsafe_PerService; +typedef AIAccessConst PerService_crat; +typedef AIAccess PerService_rat; +typedef AIAccess PerService_wat; + +} // namespace AICurlPrivate + +// We can't put threadsafe_PerService in a std::map because you can't copy a mutex. +// Therefore, use an intrusive pointer for the threadsafe type. +typedef boost::intrusive_ptr AIPerServicePtr; + +//----------------------------------------------------------------------------- +// AIPerService + +// This class provides a static interface to create and maintain instances of AIPerService objects, +// so that at any moment there is at most one instance per service (hostname:port). +// Those instances then are used to queue curl requests when the maximum number of connections +// for that service already have been reached. And to keep track of the bandwidth usage, and the +// number of queued requests in the pipeline, for this service. +class AIPerService { + private: + typedef std::map instance_map_type; + typedef AIThreadSafeSimpleDC threadsafe_instance_map_type; + typedef AIAccess instance_map_rat; + typedef AIAccess instance_map_wat; + + static threadsafe_instance_map_type sInstanceMap; // Map of AIPerService instances with the hostname as key. + + friend class AIThreadSafeSimpleDC; // threadsafe_PerService + AIPerService(void); + + public: + ~AIPerService(); + + public: + typedef instance_map_type::iterator iterator; + typedef instance_map_type::const_iterator const_iterator; + + // Utility function; extract canonical (lowercase) hostname and port from url. + static std::string extract_canonical_servicename(std::string const& url); + + // Return (possibly create) a unique instance for the given hostname. + static AIPerServicePtr instance(std::string const& servicename); + + // Release instance (object will be deleted if this was the last instance). + static void release(AIPerServicePtr& instance); + + // Remove everything. Called upon viewer exit. + static void purge(void); + + private: + typedef std::deque queued_request_type; + + int mApprovedRequests; // The number of approved requests by wantsMoreHTTPRequestsFor that were not added to the command queue yet. + int mQueuedCommands; // Number of add commands (minus remove commands) with this host in the command queue. + int mAdded; // Number of active easy handles with this host. + queued_request_type mQueuedRequests; // Waiting (throttled) requests. + + bool mQueueEmpty; // Set to true when the queue becomes precisely empty. + bool mQueueFull; // Set to true when the queue is popped and then still isn't empty; + bool mRequestStarvation; // Set to true when the queue was about to be popped but was already empty. + + AIAverage mHTTPBandwidth; // Keeps track on number of bytes received for this service in the past second. + int mConcurrectConnections; // The maximum number of allowed concurrent connections to this service. + int mMaxPipelinedRequests; // The maximum number of accepted requests for this service that didn't finish yet. + + // Global administration of the total number of queued requests of all services combined. + private: + struct TotalQueued { + S32 count; // The sum of mQueuedRequests.size() of all AIPerService objects together. + bool empty; // Set to true when count becomes precisely zero as the result of popping any queue. + bool full; // Set to true when count is still larger than zero after popping any queue. + bool starvation; // Set to true when any queue was about to be popped when count was already zero. + TotalQueued(void) : count(0), empty(false), full(false), starvation(false) { } + }; + static AIThreadSafeSimpleDC sTotalQueued; + typedef AIAccessConst TotalQueued_crat; + typedef AIAccess TotalQueued_rat; + typedef AIAccess TotalQueued_wat; + public: + static S32 total_queued_size(void) { return TotalQueued_rat(sTotalQueued)->count; } + + // Global administration of the maximum number of pipelined requests of all services combined. + private: + struct MaxPipelinedRequests { + S32 count; // The maximum total number of accepted requests that didn't finish yet. + U64 last_increment; // Last time that sMaxPipelinedRequests was incremented. + U64 last_decrement; // Last time that sMaxPipelinedRequests was decremented. + MaxPipelinedRequests(void) : count(32), last_increment(0), last_decrement(0) { } + }; + static AIThreadSafeSimpleDC sMaxPipelinedRequests; + typedef AIAccessConst MaxPipelinedRequests_crat; + typedef AIAccess MaxPipelinedRequests_rat; + typedef AIAccess MaxPipelinedRequests_wat; + public: + static void setMaxPipelinedRequests(S32 count) { MaxPipelinedRequests_wat(sMaxPipelinedRequests)->count = count; } + static void incrementMaxPipelinedRequests(S32 increment) { MaxPipelinedRequests_wat(sMaxPipelinedRequests)->count += increment; } + + // Global administration of throttle fraction (which is the same for all services). + private: + struct ThrottleFraction { + U32 fraction; // A value between 0 and 1024: each service is throttled when it uses more than max_bandwidth * (sThrottleFraction/1024) bandwidth. + AIAverage average; // Average of fraction over 25 * 40ms = 1 second. + U64 last_add; // Last time that faction was added to average. + ThrottleFraction(void) : fraction(1024), average(25), last_add(0) { } + }; + static AIThreadSafeSimpleDC sThrottleFraction; + typedef AIAccessConst ThrottleFraction_crat; + typedef AIAccess ThrottleFraction_rat; + typedef AIAccess ThrottleFraction_wat; + + static LLAtomicU32 sHTTPThrottleBandwidth125; // HTTPThrottleBandwidth times 125 (in bytes/s). + static bool sNoHTTPBandwidthThrottling; // Global override to disable bandwidth throttling. + + public: + void added_to_command_queue(void) { ++mQueuedCommands; } + void removed_from_command_queue(void) { --mQueuedCommands; llassert(mQueuedCommands >= 0); } + void added_to_multi_handle(void); // Called when an easy handle for this host has been added to the multi handle. + void removed_from_multi_handle(void); // Called when an easy handle for this host is removed again from the multi handle. + bool throttled(void) const; // Returns true if the maximum number of allowed requests for this host have been added to the multi handle. + + void queue(AICurlEasyRequest const& easy_request); // Add easy_request to the queue. + bool cancel(AICurlEasyRequest const& easy_request); // Remove easy_request from the queue (if it's there). + + void add_queued_to(AICurlPrivate::curlthread::MultiHandle* mh); + // Add queued easy handle (if any) to the multi handle. The request is removed from the queue, + // followed by either a call to added_to_multi_handle() or to queue() to add it back. + + S32 pipelined_requests(void) const { return mApprovedRequests + mQueuedCommands + mQueuedRequests.size() + mAdded; } + + AIAverage& bandwidth(void) { return mHTTPBandwidth; } + AIAverage const& bandwidth(void) const { return mHTTPBandwidth; } + + static void setNoHTTPBandwidthThrottling(bool nb) { sNoHTTPBandwidthThrottling = nb; } + static void setHTTPThrottleBandwidth(F32 max_kbps) { sHTTPThrottleBandwidth125 = 125.f * max_kbps; } + static size_t getHTTPThrottleBandwidth125(void) { return sHTTPThrottleBandwidth125; } + + // Called when CurlConcurrentConnectionsPerService changes. + static void adjust_concurrent_connections(int increment); + + // The two following functions are static and have the AIPerService object passed + // as first argument as an AIPerServicePtr because that avoids the need of having + // the AIPerService object locked for the whole duration of the call. + // The functions only lock it when access is required. + + // Returns true if curl can handle another request for this host. + // Should return false if the maximum allowed HTTP bandwidth is reached, or when + // the latency between request and actual delivery becomes too large. + static bool wantsMoreHTTPRequestsFor(AIPerServicePtr const& per_service); + // Return true if too much bandwidth is being used. + static bool checkBandwidthUsage(AIPerServicePtr const& per_service, U64 sTime_40ms); + + // A helper class to decrement mApprovedRequests after requests approved by wantsMoreHTTPRequestsFor were handled. + class Approvement { + private: + AIPerServicePtr mPerServicePtr; + bool mHonored; + public: + Approvement(AIPerServicePtr const& per_service) : mPerServicePtr(per_service), mHonored(false) { } + ~Approvement() { honored(); } + void honored(void); + }; + + private: + // Disallow copying. + AIPerService(AIPerService const&); +}; + +namespace AICurlPrivate { + +class RefCountedThreadSafePerService : public threadsafe_PerService { + public: + RefCountedThreadSafePerService(void) : mReferenceCount(0) { } + bool exactly_two_left(void) const { return mReferenceCount == 2; } + + private: + // Used by AIPerServicePtr. Object is deleted when reference count reaches zero. + LLAtomicU32 mReferenceCount; + + friend void intrusive_ptr_add_ref(RefCountedThreadSafePerService* p); + friend void intrusive_ptr_release(RefCountedThreadSafePerService* p); +}; + +extern U32 CurlConcurrentConnectionsPerService; + +} // namespace AICurlPrivate + +#endif // AICURLPERSERVICE_H diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index cfd4b5d91..de3e6b4b1 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -34,7 +34,7 @@ #include #include "llatomic.h" #include "llrefcount.h" -#include "aicurlperhost.h" +#include "aicurlperservice.h" #include "aihttptimeout.h" #include "llhttpclient.h" @@ -304,8 +304,8 @@ class CurlEasyRequest : public CurlEasyHandle { CURLcode mResult; //AIFIXME: this does not belong in the request object, but belongs in the response object. AIHTTPTimeoutPolicy const* mTimeoutPolicy; - std::string mLowercaseHostname; // Lowercase hostname (canonicalized) extracted from the url. - PerHostRequestQueuePtr mPerHostPtr; // Pointer to the corresponding PerHostRequestQueue. + std::string mLowercaseServicename; // Lowercase hostname:port (canonicalized) extracted from the url. + AIPerServicePtr mPerServicePtr; // Pointer to the corresponding AIPerService. LLPointer mTimeout;// Timeout administration object associated with last created CurlSocketInfo. bool mTimeoutIsOrphan; // Set to true when mTimeout is not (yet) associated with a CurlSocketInfo. #if defined(CWDEBUG) || defined(DEBUG_CURLIO) @@ -316,7 +316,8 @@ class CurlEasyRequest : public CurlEasyHandle { public: // These two are only valid after finalizeRequest. AIHTTPTimeoutPolicy const* getTimeoutPolicy(void) const { return mTimeoutPolicy; } - std::string const& getLowercaseHostname(void) const { return mLowercaseHostname; } + std::string const& getLowercaseServicename(void) const { return mLowercaseServicename; } + std::string getLowercaseHostname(void) const; // Called by CurlSocketInfo to allow access to the last (after a redirect) HTTPTimeout object related to this request. // This creates mTimeout (unless mTimeoutIsOrphan is set in which case it adopts the orphan). LLPointer& get_timeout_object(void); @@ -347,10 +348,10 @@ class CurlEasyRequest : public CurlEasyHandle { inline ThreadSafeBufferedCurlEasyRequest* get_lockobj(void); inline ThreadSafeBufferedCurlEasyRequest const* get_lockobj(void) const; - // PerHost API. - PerHostRequestQueuePtr getPerHostPtr(void); // (Optionally create and) return a pointer to the unique - // PerHostRequestQueue corresponding to mLowercaseHostname. - bool removeFromPerHostQueue(AICurlEasyRequest const&) const; // Remove this request from the per-host queue, if queued at all. + // PerService API. + AIPerServicePtr getPerServicePtr(void); // (Optionally create and) return a pointer to the unique + // AIPerService corresponding to mLowercaseServicename. + bool removeFromPerServiceQueue(AICurlEasyRequest const&) const; // Remove this request from the per-host queue, if queued at all. // Returns true if it was queued. protected: // Pass events to parent. @@ -374,6 +375,9 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { void resetState(void); void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, AIHTTPHeaders const& headers, LLHTTPClient::ResponderPtr responder); + // Called if this request should be queued on the curl thread when too much bandwidth is being used. + void queue_if_too_much_bandwidth_usage(void) { mQueueIfTooMuchBandwidthUsage = true; } + buffer_ptr_t& getInput(void) { return mInput; } buffer_ptr_t& getOutput(void) { return mOutput; } @@ -395,6 +399,9 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { // Post-initialization, set the parent to pass the events to. void send_buffer_events_to(AIBufferedCurlEasyRequestEvents* target) { mBufferEventsTarget = target; } + // Called whenever new body data was (might be) received. Keeps track of the used HTTP bandwidth. + void update_body_bandwidth(void); + protected: // Events from this class. /*virtual*/ void received_HTTP_header(void); @@ -410,13 +417,15 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { U32 mStatus; // HTTP status, decoded from the first header line. std::string mReason; // The "reason" from the same header line. U32 mRequestTransferedBytes; - U32 mResponseTransferedBytes; + size_t mTotalRawBytes; // Raw body data (still, possibly, compressed) received from the server so far. AIBufferedCurlEasyRequestEvents* mBufferEventsTarget; + bool mQueueIfTooMuchBandwidthUsage; // Set if the curl thread should check bandwidth usage and queue this request if too much is being used. public: static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()). static LLMutex sResponderCallbackMutex; // Locked while calling back any overridden ResponderBase::finished and/or accessing sShuttingDown. static bool sShuttingDown; // If true, no additional calls to ResponderBase::finished will be made anymore. + static AIAverage sHTTPBandwidth; // HTTP bandwidth usage of all services combined. private: // This class may only be created by constructing a ThreadSafeBufferedCurlEasyRequest. @@ -447,6 +456,9 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { // Return true when prepRequest was already called and the object has not been // invalidated as a result of calling timed_out(). bool isValid(void) const { return mResponder; } + + // Returns true when this request should be queued by the curl thread when too much bandwidth is being used. + bool queueIfTooMuchBandwidthUsage(void) const { return mQueueIfTooMuchBandwidthUsage; } }; inline ThreadSafeBufferedCurlEasyRequest* CurlEasyRequest::get_lockobj(void) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 64b35c683..cbd2bc784 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -32,10 +32,12 @@ #include "aicurlthread.h" #include "aihttptimeoutpolicy.h" #include "aihttptimeout.h" -#include "aicurlperhost.h" +#include "aicurlperservice.h" +#include "aiaverage.h" #include "lltimer.h" // ms_sleep, get_clock_count #include "llhttpstatuscodes.h" #include "llbuffer.h" +#include "llcontrol.h" #include #if !LL_WINDOWS #include @@ -264,9 +266,15 @@ void Command::reset(void) // // If at this point addRequest is called again, then it is detected that the ThreadSafeBufferedCurlEasyRequest is active. +struct command_queue_st { + std::deque commands; // The commands + size_t size; // Number of add commands in the queue minus the number of remove commands. +}; + // Multi-threaded queue for passing Command objects from the main-thread to the curl-thread. -AIThreadSafeSimpleDC > command_queue; -typedef AIAccess > command_queue_wat; +AIThreadSafeSimpleDC command_queue; // Fills 'size' with zero, because it's a global. +typedef AIAccess command_queue_wat; +typedef AIAccess command_queue_rat; AIThreadSafeDC command_being_processed; typedef AIWriteAccess command_being_processed_wat; @@ -880,7 +888,7 @@ AICurlThread* AICurlThread::sInstance = NULL; AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), mWakeUpFd_in(CURL_SOCKET_BAD), mWakeUpFd(CURL_SOCKET_BAD), - mZeroTimeout(0), mRunning(true), mWakeUpFlag(false) + mZeroTimeout(0), mWakeUpFlag(false), mRunning(true) { create_wakeup_fds(); sInstance = this; @@ -1289,7 +1297,7 @@ void AICurlThread::process_commands(AICurlMultiHandle_wat const& multi_handle_w) // Access command_queue, and move command to command_being_processed. { command_queue_wat command_queue_w(command_queue); - if (command_queue_w->empty()) + if (command_queue_w->commands.empty()) { mWakeUpFlagMutex.lock(); mWakeUpFlag = false; @@ -1297,8 +1305,22 @@ void AICurlThread::process_commands(AICurlMultiHandle_wat const& multi_handle_w) break; } // Move the next command from the queue into command_being_processed. - *command_being_processed_wat(command_being_processed) = command_queue_w->front(); - command_queue_w->pop_front(); + command_st command; + { + command_being_processed_wat command_being_processed_w(command_being_processed); + *command_being_processed_w = command_queue_w->commands.front(); + command = command_being_processed_w->command(); + } + // Update the size: the number netto number of pending requests in the command queue. + command_queue_w->commands.pop_front(); + if (command == cmd_add) + { + command_queue_w->size--; + } + else if (command == cmd_remove) + { + command_queue_w->size++; + } } // Access command_being_processed only. { @@ -1309,9 +1331,11 @@ void AICurlThread::process_commands(AICurlMultiHandle_wat const& multi_handle_w) case cmd_boost: // FIXME: future stuff break; case cmd_add: + PerService_wat(*AICurlEasyRequest_wat(*command_being_processed_r->easy_request())->getPerServicePtr())->removed_from_command_queue(); multi_handle_w->add_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request())); break; case cmd_remove: + PerService_wat(*AICurlEasyRequest_wat(*command_being_processed_r->easy_request())->getPerServicePtr())->added_to_command_queue(); // Not really, but this has the same effect as 'removed a remove command'. multi_handle_w->remove_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request()), true); break; } @@ -1520,8 +1544,8 @@ void AICurlThread::run(void) continue; } // Clock count used for timeouts. - HTTPTimeout::sClockCount = get_clock_count(); - Dout(dc::curl, "HTTPTimeout::sClockCount = " << HTTPTimeout::sClockCount); + HTTPTimeout::sTime_10ms = get_clock_count() * HTTPTimeout::sClockWidth_10ms; + Dout(dc::curl, "HTTPTimeout::sTime_10ms = " << HTTPTimeout::sTime_10ms); if (ready == 0) { multi_handle_w->socket_action(CURL_SOCKET_TIMEOUT, 0); @@ -1553,7 +1577,7 @@ void AICurlThread::run(void) multi_handle_w->check_msg_queue(); } // Clear the queued requests. - PerHostRequestQueue::purge(); + AIPerService::purge(); } AICurlMultiHandle::destroyInstance(); } @@ -1561,6 +1585,8 @@ void AICurlThread::run(void) //----------------------------------------------------------------------------- // MultiHandle +LLAtomicU32 MultiHandle::sTotalAdded; + MultiHandle::MultiHandle(void) : mTimeout(-1), mReadPollSet(NULL), mWritePollSet(NULL) { mReadPollSet = new PollSet; @@ -1653,6 +1679,7 @@ CURLMcode MultiHandle::socket_action(curl_socket_t sockfd, int ev_bitmask) } while(res == CURLM_CALL_MULTI_PERFORM); llassert(mAddedEasyRequests.size() >= (size_t)running_handles); + AICurlInterface::Stats::running_handles = running_handles; return res; } @@ -1677,30 +1704,37 @@ static U32 curl_max_total_concurrent_connections = 32; // Initialized on st void MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request) { bool throttled = true; // Default. - PerHostRequestQueuePtr per_host; + AIPerServicePtr per_service; { AICurlEasyRequest_wat curl_easy_request_w(*easy_request); - per_host = curl_easy_request_w->getPerHostPtr(); - PerHostRequestQueue_wat per_host_w(*per_host); - if (mAddedEasyRequests.size() < curl_max_total_concurrent_connections && !per_host_w->throttled()) + per_service = curl_easy_request_w->getPerServicePtr(); + bool too_much_bandwidth = curl_easy_request_w->queueIfTooMuchBandwidthUsage() && AIPerService::checkBandwidthUsage(per_service, get_clock_count() * HTTPTimeout::sClockWidth_40ms); + PerService_wat per_service_w(*per_service); + if (!too_much_bandwidth && mAddedEasyRequests.size() < curl_max_total_concurrent_connections && !per_service_w->throttled()) { curl_easy_request_w->set_timeout_opts(); if (curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle) == CURLM_OK) { - per_host_w->added_to_multi_handle(); // (About to be) added to mAddedEasyRequests. + per_service_w->added_to_multi_handle(); // (About to be) added to mAddedEasyRequests. throttled = false; // Fall through... } } } // Release the lock on easy_request. if (!throttled) { // ... to here. - std::pair res = mAddedEasyRequests.insert(easy_request); +#ifdef SHOW_ASSERT + std::pair res = +#endif + mAddedEasyRequests.insert(easy_request); llassert(res.second); // May not have been added before. - Dout(dc::curl, "MultiHandle::add_easy_request: Added AICurlEasyRequest " << (void*)easy_request.get_ptr().get() << "; now processing " << mAddedEasyRequests.size() << " easy handles."); + sTotalAdded++; + llassert(sTotalAdded == mAddedEasyRequests.size()); + Dout(dc::curl, "MultiHandle::add_easy_request: Added AICurlEasyRequest " << (void*)easy_request.get_ptr().get() << + "; now processing " << mAddedEasyRequests.size() << " easy handles [running_handles = " << AICurlInterface::Stats::running_handles << "]."); return; } // The request could not be added, we have to queue it. - PerHostRequestQueue_wat(*per_host)->queue(easy_request); + PerService_wat(*per_service)->queue(easy_request); #ifdef SHOW_ASSERT // Not active yet, but it's no longer an error if next we try to remove the request. AICurlEasyRequest_wat(*easy_request)->mRemovedPerCommand = false; @@ -1717,7 +1751,7 @@ CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request #ifdef SHOW_ASSERT bool removed = #endif - easy_request_w->removeFromPerHostQueue(easy_request); + easy_request_w->removeFromPerServiceQueue(easy_request); #ifdef SHOW_ASSERT if (removed) { @@ -1733,12 +1767,12 @@ CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator const& iter, bool as_per_command) { CURLMcode res; - PerHostRequestQueuePtr per_host; + AIPerServicePtr per_service; { AICurlEasyRequest_wat curl_easy_request_w(**iter); res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle); - per_host = curl_easy_request_w->getPerHostPtr(); - PerHostRequestQueue_wat(*per_host)->removed_from_multi_handle(); // (About to be) removed from mAddedEasyRequests. + per_service = curl_easy_request_w->getPerServicePtr(); + PerService_wat(*per_service)->removed_from_multi_handle(); // (About to be) removed from mAddedEasyRequests. #ifdef SHOW_ASSERT curl_easy_request_w->mRemovedPerCommand = as_per_command; #endif @@ -1747,12 +1781,15 @@ CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator cons ThreadSafeBufferedCurlEasyRequest* lockobj = iter->get_ptr().get(); #endif mAddedEasyRequests.erase(iter); + --sTotalAdded; + llassert(sTotalAdded == mAddedEasyRequests.size()); #if CWDEBUG - Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)lockobj << "; now processing " << mAddedEasyRequests.size() << " easy handles."); + Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)lockobj << + "; now processing " << mAddedEasyRequests.size() << " easy handles [running_handles = " << AICurlInterface::Stats::running_handles << "]."); #endif // Attempt to add a queued request, if any. - PerHostRequestQueue_wat(*per_host)->add_queued_to(this); + PerService_wat(*per_service)->add_queued_to(this); return res; } @@ -1795,6 +1832,8 @@ void MultiHandle::check_msg_queue(void) void MultiHandle::finish_easy_request(AICurlEasyRequest const& easy_request, CURLcode result) { AICurlEasyRequest_wat curl_easy_request_w(*easy_request); + // Final body bandwidth update. + curl_easy_request_w->update_body_bandwidth(); // Store the result in the easy handle. curl_easy_request_w->storeResult(result); #ifdef CWDEBUG @@ -1926,7 +1965,8 @@ void clearCommandQueue(void) { // Clear the command queue now in order to avoid the global deinitialization order fiasco. command_queue_wat command_queue_w(command_queue); - command_queue_w->clear(); + command_queue_w->commands.clear(); + command_queue_w->size = 0; } //----------------------------------------------------------------------------- @@ -1988,10 +2028,10 @@ void BufferedCurlEasyRequest::processOutput(void) if (responseCode == HTTP_INTERNAL_ERROR_LOW_SPEED) { // Rewrite error to something understandable. - responseReason = llformat("Connection to \"%s\" stalled: download speed dropped below %u bytes/s for %u seconds (up till that point, %s received a total of %u bytes). " + responseReason = llformat("Connection to \"%s\" stalled: download speed dropped below %u bytes/s for %u seconds (up till that point, %s received a total of %lu bytes). " "To change these values, go to Advanced --> Debug Settings and change CurlTimeoutLowSpeedLimit and CurlTimeoutLowSpeedTime respectively.", mResponder->getURL().c_str(), mResponder->getHTTPTimeoutPolicy().getLowSpeedLimit(), mResponder->getHTTPTimeoutPolicy().getLowSpeedTime(), - mResponder->getName(), mResponseTransferedBytes); + mResponder->getName(), mTotalRawBytes); } setopt(CURLOPT_FRESH_CONNECT, TRUE); } @@ -2058,7 +2098,9 @@ size_t BufferedCurlEasyRequest::curlWriteCallback(char* data, size_t size, size_ // BufferedCurlEasyRequest::setBodyLimit is never called, so buffer_w->mBodyLimit is infinite. //S32 bytes = llmin(size * nmemb, buffer_w->mBodyLimit); buffer_w->mBodyLimit -= bytes; self_w->getOutput()->append(sChannels.in(), (U8 const*)data, bytes); - self_w->mResponseTransferedBytes += bytes; // Accumulate data received from the server. + // Update HTTP bandwith. + self_w->update_body_bandwidth(); + // Update timeout administration. if (self_w->httptimeout()->data_received(bytes)) // Update timeout administration. { // Transfer timed out. Return 0 which will abort with error CURLE_WRITE_ERROR. @@ -2067,6 +2109,25 @@ size_t BufferedCurlEasyRequest::curlWriteCallback(char* data, size_t size, size_ return bytes; } +void BufferedCurlEasyRequest::update_body_bandwidth(void) +{ + double size_download; // Total amount of raw bytes received so far (ie. still compressed, 'bytes' is uncompressed). + getinfo(CURLINFO_SIZE_DOWNLOAD, &size_download); + size_t total_raw_bytes = size_download; + size_t raw_bytes = total_raw_bytes - mTotalRawBytes; + mTotalRawBytes = total_raw_bytes; + // Note that in some cases (like HTTP_PARTIAL_CONTENT), the output of CURLINFO_SIZE_DOWNLOAD lags + // behind and will return 0 the first time, and the value of the previous chunk the next time. + // The last call from MultiHandle::finish_easy_request recorrects this, in that case. + if (raw_bytes > 0) + { + U64 const sTime_40ms = curlthread::HTTPTimeout::sTime_10ms >> 2; + AIAverage& http_bandwidth(PerService_wat(*getPerServicePtr())->bandwidth()); + http_bandwidth.addData(raw_bytes, sTime_40ms); + sHTTPBandwidth.addData(raw_bytes, sTime_40ms); + } +} + //static size_t BufferedCurlEasyRequest::curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data) { @@ -2124,7 +2185,7 @@ size_t BufferedCurlEasyRequest::curlHeaderCallback(char* data, size_t size, size std::string::iterator pos3 = std::find(pos2, end, '\r'); U32 status = 0; std::string reason; - if (pos3 != end && std::isdigit(*pos1)) + if (pos3 != end && LLStringOps::isDigit(*pos1)) { status = atoi(&header_line[pos1 - begin]); reason.assign(pos2, pos3); @@ -2154,6 +2215,11 @@ size_t BufferedCurlEasyRequest::curlHeaderCallback(char* data, size_t size, size self_w->httptimeout()->being_redirected(); } } + // Update HTTP bandwidth. + U64 const sTime_40ms = curlthread::HTTPTimeout::sTime_10ms >> 2; + AIAverage& http_bandwidth(PerService_wat(*self_w->getPerServicePtr())->bandwidth()); + http_bandwidth.addData(header_len, sTime_40ms); + sHTTPBandwidth.addData(header_len, sTime_40ms); // Update timeout administration. This must be done after the status is already known. if (self_w->httptimeout()->data_received(header_len/*,*/ ASSERT_ONLY_COMMA(self_w->upload_error_status()))) { @@ -2328,7 +2394,7 @@ void AICurlEasyRequest::addRequest(void) // Find the last command added. command_st cmd = cmd_none; - for (std::deque::iterator iter = command_queue_w->begin(); iter != command_queue_w->end(); ++iter) + for (std::deque::iterator iter = command_queue_w->commands.begin(); iter != command_queue_w->commands.end(); ++iter) { if (*iter == *this) { @@ -2354,8 +2420,11 @@ void AICurlEasyRequest::addRequest(void) } #endif // Add a command to add the new request to the multi session to the command queue. - command_queue_w->push_back(Command(*this, cmd_add)); - AICurlEasyRequest_wat(*get())->add_queued(); + command_queue_w->commands.push_back(Command(*this, cmd_add)); + command_queue_w->size++; + AICurlEasyRequest_wat curl_easy_request_w(*get()); + PerService_wat(*curl_easy_request_w->getPerServicePtr())->added_to_command_queue(); + curl_easy_request_w->add_queued(); } // Something was added to the queue, wake up the thread to get it. wakeUpCurlThread(); @@ -2378,7 +2447,7 @@ void AICurlEasyRequest::removeRequest(void) // Find the last command added. command_st cmd = cmd_none; - for (std::deque::iterator iter = command_queue_w->begin(); iter != command_queue_w->end(); ++iter) + for (std::deque::iterator iter = command_queue_w->commands.begin(); iter != command_queue_w->commands.end(); ++iter) { if (*iter == *this) { @@ -2415,9 +2484,12 @@ void AICurlEasyRequest::removeRequest(void) } #endif // Add a command to remove this request from the multi session to the command queue. - command_queue_w->push_back(Command(*this, cmd_remove)); + command_queue_w->commands.push_back(Command(*this, cmd_remove)); + command_queue_w->size--; + AICurlEasyRequest_wat curl_easy_request_w(*get()); + PerService_wat(*curl_easy_request_w->getPerServicePtr())->removed_from_command_queue(); // Note really, but this has the same effect as 'added a remove command'. // Suppress warning that would otherwise happen if the callbacks are revoked before the curl thread removed the request. - AICurlEasyRequest_wat(*get())->remove_queued(); + curl_easy_request_w->remove_queued(); } // Something was added to the queue, wake up the thread to get it. wakeUpCurlThread(); @@ -2427,7 +2499,9 @@ void AICurlEasyRequest::removeRequest(void) namespace AICurlInterface { -void startCurlThread(U32 CurlMaxTotalConcurrentConnections, U32 CurlConcurrentConnectionsPerHost, bool NoVerifySSLCert) +LLControlGroup* sConfigGroup; + +void startCurlThread(LLControlGroup* control_group) { using namespace AICurlPrivate; using namespace AICurlPrivate::curlthread; @@ -2435,9 +2509,12 @@ void startCurlThread(U32 CurlMaxTotalConcurrentConnections, U32 CurlConcurrentCo llassert(is_main_thread()); // Cache Debug Settings. - curl_max_total_concurrent_connections = CurlMaxTotalConcurrentConnections; - curl_concurrent_connections_per_host = CurlConcurrentConnectionsPerHost; - gNoVerifySSLCert = NoVerifySSLCert; + sConfigGroup = control_group; + curl_max_total_concurrent_connections = sConfigGroup->getU32("CurlMaxTotalConcurrentConnections"); + CurlConcurrentConnectionsPerService = sConfigGroup->getU32("CurlConcurrentConnectionsPerService"); + gNoVerifySSLCert = sConfigGroup->getBOOL("NoVerifySSLCert"); + AIPerService::setMaxPipelinedRequests(curl_max_total_concurrent_connections); + AIPerService::setHTTPThrottleBandwidth(sConfigGroup->getF32("HTTPThrottleBandwidth")); AICurlThread::sInstance = new AICurlThread; AICurlThread::sInstance->start(); @@ -2445,19 +2522,24 @@ void startCurlThread(U32 CurlMaxTotalConcurrentConnections, U32 CurlConcurrentCo bool handleCurlMaxTotalConcurrentConnections(LLSD const& newvalue) { + using namespace AICurlPrivate; using namespace AICurlPrivate::curlthread; + U32 old = curl_max_total_concurrent_connections; curl_max_total_concurrent_connections = newvalue.asInteger(); + AIPerService::incrementMaxPipelinedRequests(curl_max_total_concurrent_connections - old); llinfos << "CurlMaxTotalConcurrentConnections set to " << curl_max_total_concurrent_connections << llendl; return true; } -bool handleCurlConcurrentConnectionsPerHost(LLSD const& newvalue) +bool handleCurlConcurrentConnectionsPerService(LLSD const& newvalue) { using namespace AICurlPrivate; - curl_concurrent_connections_per_host = newvalue.asInteger(); - llinfos << "CurlConcurrentConnectionsPerHost set to " << curl_concurrent_connections_per_host << llendl; + U32 new_concurrent_connections = newvalue.asInteger(); + AIPerService::adjust_concurrent_connections(new_concurrent_connections - CurlConcurrentConnectionsPerService); + CurlConcurrentConnectionsPerService = new_concurrent_connections; + llinfos << "CurlConcurrentConnectionsPerService set to " << CurlConcurrentConnectionsPerService << llendl; return true; } @@ -2467,5 +2549,245 @@ bool handleNoVerifySSLCert(LLSD const& newvalue) return true; } +U32 getNumHTTPCommands(void) +{ + using namespace AICurlPrivate; + + command_queue_rat command_queue_r(command_queue); + return command_queue_r->size; +} + +U32 getNumHTTPQueued(void) +{ + return AIPerService::total_queued_size(); +} + +U32 getNumHTTPAdded(void) +{ + return AICurlPrivate::curlthread::MultiHandle::total_added_size(); +} + +size_t getHTTPBandwidth(void) +{ + using namespace AICurlPrivate; + + U64 const sTime_40ms = get_clock_count() * curlthread::HTTPTimeout::sClockWidth_40ms; + return BufferedCurlEasyRequest::sHTTPBandwidth.truncateData(sTime_40ms); +} + } // namespace AICurlInterface +// Global AIPerService members. +AIThreadSafeSimpleDC AIPerService::sMaxPipelinedRequests; +AIThreadSafeSimpleDC AIPerService::sThrottleFraction; +LLAtomicU32 AIPerService::sHTTPThrottleBandwidth125(250000); +bool AIPerService::sNoHTTPBandwidthThrottling; + +// Return true if we want at least one more HTTP request for this host. +// +// It's OK if this function is a bit fuzzy, but we don't want it to return +// true a hundred times on a row when it is called fast in a loop. +// Hence the following consideration: +// +// This function is called only from LLTextureFetchWorker::doWork, and when it returns true +// then doWork will call LLHTTPClient::request with a NULL default engine (signaling that +// it is OK to run in any thread). +// +// At the end, LLHTTPClient::request calls AIStateMachine::run, which in turn calls +// AIStateMachine::reset at the end. Because NULL is passed as default_engine, reset will +// call AIStateMachine::multiplex to immediately start running the state machine. This +// causes it to go through the states bs_reset, bs_initialize and then bs_multiplex with +// run state AICurlEasyRequestStateMachine_addRequest. Finally, in this state, multiplex +// calls AICurlEasyRequestStateMachine::multiplex_impl which then calls AICurlEasyRequest::addRequest +// which causes an increment of command_queue_w->size and AIPerService::mQueuedCommands. +// +// It is therefore guaranteed that in one loop of LLTextureFetchWorker::doWork, +// this size is incremented; stopping this function from returning true once we reached the +// threshold of "pipelines" requests (the sum of requests in the command queue, the ones +// throttled and queued in AIPerService::mQueuedRequests and the already +// running requests (in MultiHandle::mAddedEasyRequests)). +// +//static +bool AIPerService::wantsMoreHTTPRequestsFor(AIPerServicePtr const& per_service) +{ + using namespace AICurlPrivate; + using namespace AICurlPrivate::curlthread; + + // Do certain things at most once every 40ms. + U64 const sTime_40ms = get_clock_count() * HTTPTimeout::sClockWidth_40ms; // Time in 40ms units. + + // Cache all sTotalQueued info. + bool starvation, decrement_threshold; + S32 total_queued_or_added = MultiHandle::total_added_size(); + { + TotalQueued_wat total_queued_w(sTotalQueued); + total_queued_or_added += total_queued_w->count; + starvation = total_queued_w->starvation; + decrement_threshold = total_queued_w->full && !total_queued_w->empty; + total_queued_w->empty = total_queued_w->full = false; // Reset flags. + } + + // Whether or not we're going to approve a new request, decrement the global threshold first, when appropriate. + + if (decrement_threshold) + { + MaxPipelinedRequests_wat max_pipelined_requests_w(sMaxPipelinedRequests); + if (max_pipelined_requests_w->count > (S32)curl_max_total_concurrent_connections && + sTime_40ms > max_pipelined_requests_w->last_decrement) + { + // Decrement the threshold because since the last call to this function at least one curl request finished + // and was replaced with another request from the queue, but the queue never ran empty: we have too many + // queued requests. + max_pipelined_requests_w->count--; + // Do this at most once every 40 ms. + max_pipelined_requests_w->last_decrement = sTime_40ms; + } + } + + // Check if it's ok to get a new request for this particular service and update the per-service threshold. + + bool reject, equal, increment_threshold; + { + PerService_wat per_service_w(*per_service); + S32 const pipelined_requests_per_service = per_service_w->pipelined_requests(); + //llassert(pipelined_requests_per_service >= 0 && pipelined_requests_per_service <= 16); + reject = pipelined_requests_per_service >= per_service_w->mMaxPipelinedRequests; + equal = pipelined_requests_per_service == per_service_w->mMaxPipelinedRequests; + increment_threshold = per_service_w->mRequestStarvation; + decrement_threshold = per_service_w->mQueueFull && !per_service_w->mQueueEmpty; + // Reset flags. + per_service_w->mQueueFull = per_service_w->mQueueEmpty = per_service_w->mRequestStarvation = false; + if (decrement_threshold) + { + if (per_service_w->mMaxPipelinedRequests > per_service_w->mConcurrectConnections) + { + per_service_w->mMaxPipelinedRequests--; + } + } + else if (increment_threshold && reject) + { + if (per_service_w->mMaxPipelinedRequests < 2 * per_service_w->mConcurrectConnections) + { + per_service_w->mMaxPipelinedRequests++; + // Immediately take the new threshold into account. + reject = !equal; + } + } + if (!reject) + { + // Before releasing the lock on per_service, stop other threads from getting a + // too small value from pipelined_requests() and approving too many requests. + per_service_w->mApprovedRequests++; + //llassert(per_service_w->mApprovedRequests <= 16); + } + } + if (reject) + { + // Too many request for this service already. + return false; + } + + // Throttle on bandwidth usage. + if (checkBandwidthUsage(per_service, sTime_40ms)) + { + // Too much bandwidth is being used, either in total or for this service. + PerService_wat(*per_service)->mApprovedRequests--; // Not approved after all. + return false; + } + + // Check if it's ok to get a new request based on the total number of requests and increment the threshold if appropriate. + + S32 const pipelined_requests = command_queue_rat(command_queue)->size + total_queued_or_added; + // We can't take the command being processed (command_being_processed) into account without + // introducing relatively long waiting times for some mutex (namely between when the command + // is moved from command_queue to command_being_processed, till it's actually being added to + // mAddedEasyRequests). The whole purpose of command_being_processed is to reduce the time + // that things are locked to micro seconds, so we'll just accept an off-by-one fuzziness + // here instead. + + // The maximum number of requests that may be queued in command_queue is equal to the total number of requests + // that may exist in the pipeline minus the number of requests queued in AIPerService objects, minus + // the number of already running requests. + MaxPipelinedRequests_wat max_pipelined_requests_w(sMaxPipelinedRequests); + reject = pipelined_requests >= max_pipelined_requests_w->count; + equal = pipelined_requests == max_pipelined_requests_w->count; + increment_threshold = starvation; + if (increment_threshold && reject) + { + if (max_pipelined_requests_w->count < 2 * (S32)curl_max_total_concurrent_connections && + sTime_40ms > max_pipelined_requests_w->last_increment) + { + max_pipelined_requests_w->count++; + max_pipelined_requests_w->last_increment = sTime_40ms; + // Immediately take the new threshold into account. + reject = !equal; + } + } + if (reject) + { + PerService_wat(*per_service)->mApprovedRequests--; // Not approved after all. + } + return !reject; +} + +bool AIPerService::checkBandwidthUsage(AIPerServicePtr const& per_service, U64 sTime_40ms) +{ + if (sNoHTTPBandwidthThrottling) + return false; + + using namespace AICurlPrivate; + + // Truncate the sums to the last second, and get their value. + size_t const max_bandwidth = AIPerService::getHTTPThrottleBandwidth125(); + size_t const total_bandwidth = BufferedCurlEasyRequest::sHTTPBandwidth.truncateData(sTime_40ms); // Bytes received in the past second. + size_t const service_bandwidth = PerService_wat(*per_service)->bandwidth().truncateData(sTime_40ms); // Idem for just this service. + ThrottleFraction_wat throttle_fraction_w(sThrottleFraction); + if (sTime_40ms > throttle_fraction_w->last_add) + { + throttle_fraction_w->average.addData(throttle_fraction_w->fraction, sTime_40ms); + // Only add throttle_fraction_w->fraction once every 40 ms at most. + // It's ok to ignore other values in the same 40 ms because the value only changes on the scale of 1 second. + throttle_fraction_w->last_add = sTime_40ms; + } + double fraction_avg = throttle_fraction_w->average.getAverage(1024.0); // throttle_fraction_w->fraction averaged over the past second, or 1024 if there is no data. + + // Adjust the fraction based on total bandwidth usage. + if (total_bandwidth == 0) + throttle_fraction_w->fraction = 1024; + else + { + // This is the main formula. It can be made plausible by assuming + // an equilibrium where total_bandwidth == max_bandwidth and + // thus fraction == fraction_avg for more than a second. + // + // Then, more bandwidth is being used (for example because another + // service starts downloading). Assuming that all services that use + // a significant portion of the bandwidth, the new service included, + // must be throttled (all using the same bandwidth; note that the + // new service is immediately throttled at the same value), then + // the limit should be reduced linear with the fraction: + // max_bandwidth / total_bandwidth. + // + // For example, let max_bandwidth be 1. Let there be two throttled + // services, each using 0.5 (fraction_avg = 1024/2). Let the new + // service use what it can: also 0.5 - then without reduction the + // total_bandwidth would become 1.5, and fraction would + // become (1024/2) * 1/1.5 = 1024/3: from 2 to 3 services. + // + // In reality, total_bandwidth would rise linear from 1.0 to 1.5 in + // one second if the throttle fraction wasn't changed. However it is + // changed here. The end result is that any change more or less + // linear fades away in one second. + throttle_fraction_w->fraction = llmin(1024., fraction_avg * max_bandwidth / total_bandwidth + 0.5); + } + if (total_bandwidth > max_bandwidth) + { + throttle_fraction_w->fraction *= 0.95; + } + + // Throttle this service if it uses too much bandwidth. + // Warning: this requires max_bandwidth * 1024 to fit in a size_t. + // On 32 bit that means that HTTPThrottleBandwidth must be less than 33554 kbps. + return (service_bandwidth > (max_bandwidth * throttle_fraction_w->fraction / 1024)); +} + diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h index fc29ac1fc..9f71b015c 100644 --- a/indra/llmessage/aicurlthread.h +++ b/indra/llmessage/aicurlthread.h @@ -75,12 +75,13 @@ class MultiHandle : public CurlMultiHandle typedef std::set addedEasyRequests_type; addedEasyRequests_type mAddedEasyRequests; // All easy requests currently added to the multi handle. long mTimeout; // The last timeout in ms as set by the callback CURLMOPT_TIMERFUNCTION. + static LLAtomicU32 sTotalAdded; // The (sum of the) size of mAddedEasyRequests (of every MultiHandle, but there is only one). private: // Store result and trigger events for easy request. void finish_easy_request(AICurlEasyRequest const& easy_request, CURLcode result); // Remove easy request at iter (must exist). - // Note that it's possible that a new request from a PerHostRequestQueue::mQueuedRequests is inserted before iter. + // Note that it's possible that a new request from a AIPerService::mQueuedRequests is inserted before iter. CURLMcode remove_easy_request(addedEasyRequests_type::iterator const& iter, bool as_per_command); static int socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp); @@ -96,6 +97,9 @@ class MultiHandle : public CurlMultiHandle // Called from the main loop every time select() timed out. void handle_stalls(void); + // Return the total number of added curl requests. + static U32 total_added_size(void) { return sTotalAdded; } + public: //----------------------------------------------------------------------------- // Curl socket administration: diff --git a/indra/llmessage/aihttptimeout.cpp b/indra/llmessage/aihttptimeout.cpp index 0f99e23d2..22dbb1cfd 100644 --- a/indra/llmessage/aihttptimeout.cpp +++ b/indra/llmessage/aihttptimeout.cpp @@ -75,6 +75,7 @@ namespace AICurlPrivate { class BufferedCurlEasyRequest { public: char const* getLowercaseHostname(void) const { return "hostname.com"; } + char const* getLowercaseServicename(void) const { return "hostname.com:12047"; } void getinfo(const int&, double* p) { *p = 0.1; } }; @@ -96,8 +97,9 @@ namespace curlthread { // HTTPTimeout //static -F64 const HTTPTimeout::sClockWidth = 1.0 / calc_clock_frequency(); // Time between two clock ticks, in seconds. -U64 HTTPTimeout::sClockCount; // Clock count, set once per select() exit. +F64 const HTTPTimeout::sClockWidth_10ms = 100.0 / calc_clock_frequency(); // Time between two clock ticks, in 10ms units. +F64 const HTTPTimeout::sClockWidth_40ms = HTTPTimeout::sClockWidth_10ms * 0.25; // Time between two clock ticks, in 40ms units. +U64 HTTPTimeout::sTime_10ms; // Time in 10ms units, set once per select() exit. // CURL-THREAD // This is called when body data was sent to the server socket. @@ -125,7 +127,7 @@ bool HTTPTimeout::data_sent(size_t n, bool finished) // | | void HTTPTimeout::reset_lowspeed(void) { - mLowSpeedClock = sClockCount; + mLowSpeedClock = sTime_10ms; mLowSpeedOn = true; mLastBytesSent = false; // We're just starting! mLastSecond = -1; // This causes lowspeed to initialize the rest. @@ -162,8 +164,8 @@ void HTTPTimeout::upload_finished(void) // We finished uploading (if there was a body to upload at all), so no more transfer rate timeouts. mLowSpeedOn = false; // Timeout if the server doesn't reply quick enough. - mStalled = sClockCount + mPolicy->getReplyDelay() / sClockWidth; - DoutCurl("upload_finished: mStalled set to sClockCount (" << sClockCount << ") + " << (mStalled - sClockCount) << " (" << mPolicy->getReplyDelay() << " seconds)"); + mStalled = sTime_10ms + 100 * mPolicy->getReplyDelay(); + DoutCurl("upload_finished: mStalled set to Time_10ms (" << sTime_10ms << ") + " << (mStalled - sTime_10ms) << " (" << mPolicy->getReplyDelay() << " seconds)"); } // CURL-THREAD @@ -230,8 +232,7 @@ bool HTTPTimeout::lowspeed(size_t bytes, bool finished) // less than low_speed_limit, we abort. // When are we? - S32 second = (sClockCount - mLowSpeedClock) * sClockWidth; - llassert(sClockWidth > 0.0); + S32 second = (sTime_10ms - mLowSpeedClock) / 100; // This REALLY should never happen, but due to another bug it did happened // and caused something so evil and hard to find that... NEVER AGAIN! llassert(second >= 0); @@ -315,8 +316,8 @@ bool HTTPTimeout::lowspeed(size_t bytes, bool finished) // Just give these bytes 4 more seconds to be written to the socket (after which we'll // assume that the 'upload finished' detection failed and we'll wait another ReplyDelay // seconds before finally, actually timing out. - mStalled = sClockCount + 4 / sClockWidth; - DoutCurl("mStalled set to sClockCount (" << sClockCount << ") + " << (mStalled - sClockCount) << " (4 seconds)"); + mStalled = sTime_10ms + 400; // 4 seconds into the future. + DoutCurl("mStalled set to sTime_10ms (" << sTime_10ms << ") + 400 (4 seconds)"); return false; } // The average transfer rate over the passed low_speed_time seconds is too low. Abort the transfer. @@ -368,8 +369,8 @@ bool HTTPTimeout::lowspeed(size_t bytes, bool finished) while(total_bytes >= mintotalbytes); } // If this function isn't called again within max_stall_time seconds, we stalled. - mStalled = sClockCount + max_stall_time / sClockWidth; - DoutCurl("mStalled set to sClockCount (" << sClockCount << ") + " << (mStalled - sClockCount) << " (" << max_stall_time << " seconds)"); + mStalled = sTime_10ms + 100 * max_stall_time; + DoutCurl("mStalled set to sTime_10ms (" << sTime_10ms << ") + " << (mStalled - sTime_10ms) << " (" << max_stall_time << " seconds)"); return false; } @@ -435,7 +436,7 @@ bool HTTPTimeout::maybe_upload_finished(void) void HTTPTimeout::print_diagnostics(CurlEasyRequest const* curl_easy_request, char const* eff_url) { #ifndef HTTPTIMEOUT_TESTSUITE - llwarns << "Request to \"" << curl_easy_request->getLowercaseHostname() << "\" timed out for " << curl_easy_request->getTimeoutPolicy()->name() << llendl; + llwarns << "Request to \"" << curl_easy_request->getLowercaseServicename() << "\" timed out for " << curl_easy_request->getTimeoutPolicy()->name() << llendl; llinfos << "Effective URL: \"" << eff_url << "\"." << llendl; double namelookup_time, connect_time, appconnect_time, pretransfer_time, starttransfer_time; curl_easy_request->getinfo(CURLINFO_NAMELOOKUP_TIME, &namelookup_time); diff --git a/indra/llmessage/aihttptimeout.h b/indra/llmessage/aihttptimeout.h index 2c5442075..1af96c9c7 100644 --- a/indra/llmessage/aihttptimeout.h +++ b/indra/llmessage/aihttptimeout.h @@ -85,11 +85,12 @@ class HTTPTimeout : public LLRefCount { S32 mLastSecond; // The time at which lowspeed() was last called, in seconds since mLowSpeedClock. S32 mOverwriteSecond; // The second at which the first bucket of this transfer will be overwritten. U32 mTotalBytes; // The sum of all bytes in mBuckets. - U64 mLowSpeedClock; // Clock count at which low speed detection (re)started. - U64 mStalled; // The clock count at which this transaction is considered to be stalling if nothing is transfered anymore. + U64 mLowSpeedClock; // The time (sTime_10ms) at which low speed detection (re)started. + U64 mStalled; // The time (sTime_10ms) at which this transaction is considered to be stalling if nothing is transfered anymore. public: - static F64 const sClockWidth; // Time between two clock ticks in seconds. - static U64 sClockCount; // Clock count used as 'now' during one loop of the main loop. + static F64 const sClockWidth_10ms; // Time between two clock ticks in 10 ms units. + static F64 const sClockWidth_40ms; // Time between two clock ticks in 40 ms units. + static U64 sTime_10ms; // Time since the epoch in 10 ms units. #if defined(CWDEBUG) || defined(DEBUG_CURLIO) ThreadSafeBufferedCurlEasyRequest* mLockObj; #endif @@ -121,7 +122,7 @@ class HTTPTimeout : public LLRefCount { void done(AICurlEasyRequest_wat const& curlEasyRequest_w, CURLcode code); // Returns true when we REALLY timed out. Might call upload_finished heuristically. - bool has_stalled(void) { return mStalled < sClockCount && !maybe_upload_finished(); } + bool has_stalled(void) { return mStalled < sTime_10ms && !maybe_upload_finished(); } // Called from BufferedCurlEasyRequest::processOutput if a timeout occurred. void print_diagnostics(CurlEasyRequest const* curl_easy_request, char const* eff_url); diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index c130938d2..d5032e8d7 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -208,7 +208,8 @@ void LLHTTPClient::request( EAllowCompressedReply allow_compression, AIStateMachine* parent, AIStateMachine::state_type new_parent_state, - AIEngine* default_engine) + AIEngine* default_engine, + bool queue_if_too_much_bandwidth_usage) { llassert(responder); @@ -221,7 +222,7 @@ void LLHTTPClient::request( LLURLRequest* req; try { - req = new LLURLRequest(method, url, body_injector, responder, headers, keepalive, does_auth, allow_compression); + req = new LLURLRequest(method, url, body_injector, responder, headers, keepalive, does_auth, allow_compression, queue_if_too_much_bandwidth_usage); #ifdef DEBUG_CURLIO req->mCurlEasyRequest.debug(debug); #endif @@ -701,6 +702,11 @@ void LLHTTPClient::post(std::string const& url, LLSD const& body, ResponderPtr r request(url, HTTP_POST, new LLSDInjector(body), responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, no_does_authentication, allow_compressed_reply, parent, new_parent_state); } +void LLHTTPClient::post_nb(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug), EKeepAlive keepalive, AIStateMachine* parent, AIStateMachine::state_type new_parent_state) +{ + request(url, HTTP_POST, new LLSDInjector(body), responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, no_does_authentication, allow_compressed_reply, parent, new_parent_state, &gMainThreadEngine, false); +} + void LLHTTPClient::postXMLRPC(std::string const& url, XMLRPC_REQUEST xmlrpc_request, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug), EKeepAlive keepalive) { request(url, HTTP_POST, new XMLRPCInjector(xmlrpc_request), responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, does_authentication, allow_compressed_reply); diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 6dc10080c..05e028f04 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -433,7 +433,8 @@ public: EAllowCompressedReply allow_compression = allow_compressed_reply, AIStateMachine* parent = NULL, /*AIStateMachine::state_type*/ U32 new_parent_state = 0, - AIEngine* default_engine = &gMainThreadEngine); + AIEngine* default_engine = &gMainThreadEngine, + bool queue_if_too_much_bandwidth_usage = true); /** @name non-blocking API */ //@{ @@ -465,6 +466,10 @@ public: static void post(std::string const& url, LLSD const& body, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive, AIStateMachine* parent = NULL, U32 new_parent_state = 0) { AIHTTPHeaders headers; post(url, body, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, parent, new_parent_state); } + static void post_nb(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive, AIStateMachine* parent = NULL, U32 new_parent_state = 0); + static void post_nb(std::string const& url, LLSD const& body, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive, AIStateMachine* parent = NULL, U32 new_parent_state = 0) + { AIHTTPHeaders headers; post_nb(url, body, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, parent, new_parent_state); } + /** Takes ownership of request and deletes it when sent */ static void postXMLRPC(std::string const& url, XMLRPC_REQUEST request, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive); static void postXMLRPC(std::string const& url, XMLRPC_REQUEST request, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive) diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index c8be0939b..2d4438804 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -75,10 +75,15 @@ std::string LLURLRequest::actionAsVerb(LLURLRequest::ERequestAction action) // This might throw AICurlNoEasyHandle. LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, std::string const& url, Injector* body, - LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, bool keepalive, bool is_auth, bool compression) : + LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, bool keepalive, bool is_auth, bool compression, + bool queue_if_too_much_bandwidth_usage) : mAction(action), mURL(url), mKeepAlive(keepalive), mIsAuth(is_auth), mNoCompression(!compression), mBody(body), mResponder(responder), mHeaders(headers), mResponderNameCache(responder ? responder->getName() : "") { + if (queue_if_too_much_bandwidth_usage) + { + AICurlEasyRequest_wat(*mCurlEasyRequest)->queue_if_too_much_bandwidth_usage(); + } } void LLURLRequest::initialize_impl(void) diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 1f9c09914..d31cc1918 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -64,7 +64,9 @@ class LLURLRequest : public AICurlEasyRequestStateMachine { * @param action One of the ERequestAction enumerations. * @param url The url of the request. It should already be encoded. */ - LLURLRequest(ERequestAction action, std::string const& url, Injector* body, LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, bool keepalive, bool is_auth, bool no_compression); + LLURLRequest(ERequestAction action, std::string const& url, Injector* body, + LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, + bool keepalive, bool is_auth, bool no_compression, bool queue_if_too_much_bandwidth_usage); /** * @brief Cached value of responder->getName() as passed to the constructor. diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 8f60d2a7d..814d407d0 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -50,7 +50,6 @@ #include "lldarray.h" #include "lldir.h" #include "llerror.h" -#include "llerrorlegacy.h" #include "llfasttimer.h" #include "llhttpclient.h" #include "llhttpnodeadapter.h" diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt index e377434b1..381782ed6 100644 --- a/indra/llplugin/slplugin/CMakeLists.txt +++ b/indra/llplugin/slplugin/CMakeLists.txt @@ -6,7 +6,6 @@ include(LLPlugin) include(Linking) include(PluginAPI) include(LLMessage) -include(GooglePerfTools) include_directories( ${LLPLUGIN_INCLUDE_DIRS} diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c5dcff371..f3cc6d444 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -38,7 +38,6 @@ include(LLXML) #include(LScript) include(Linking) include(NDOF) -include(GooglePerfTools) include(StateMachine) include(TemplateCheck) include(UI) @@ -90,6 +89,7 @@ set(viewer_SOURCE_FILES floaterao.cpp floaterlocalassetbrowse.cpp floatervoicelicense.cpp + generichandlers.cpp hbfloatergrouptitles.cpp hippofloaterxml.cpp hippogridmanager.cpp @@ -185,6 +185,7 @@ set(viewer_SOURCE_FILES llfloaterbuycontents.cpp llfloaterbuycurrency.cpp llfloaterbuyland.cpp + llfloaterbvhpreview.cpp llfloatercamera.cpp llfloaterchat.cpp llfloaterchatterbox.cpp @@ -591,6 +592,7 @@ set(viewer_HEADER_FILES floaterao.h floaterlocalassetbrowse.h floatervoicelicense.h + generichandlers.h hbfloatergrouptitles.h hippofloaterxml.h hippogridmanager.h @@ -687,6 +689,7 @@ set(viewer_HEADER_FILES llfloaterbuycontents.h llfloaterbuycurrency.h llfloaterbuyland.h + llfloaterbvhpreview.h llfloatercamera.h llfloaterchat.h llfloaterchatterbox.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 35d0a6773..98336d37a 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -171,44 +171,14 @@ Value 1 - - HTTPRequestRate - - Comment - Number of HTTP texture requests fired per second. - Persist - 1 - Type - U32 - Value - 30 - - HTTPMaxRequests - - Comment - Maximum number of simultaneous HTTP requests in progress. - Persist - 1 - Type - U32 - Value - 12 - - HTTPMinRequests - - Comment - Attempt to maintain at least this many HTTP requests in progress by ignoring bandwidth - Persist - 1 - Type - U32 - Value - 2 - + HTTPThrottleBandwidth Comment - The bandwidth (in kbit/s) to strive for + The bandwidth (in kbit/s) to strive for. Smaller values might reduce network + congestion (sim ping time, aka avatar responsiveness). Higher values might download + textures and the inventory faster, although in some cases a too high value might + actually slow that down due to serverside throttling. If unsure, choose 2000. Persist 1 Type @@ -4503,16 +4473,16 @@ This should be as low as possible, but too low may break functionality Value 64 - CurlConcurrentConnectionsPerHost + CurlConcurrentConnectionsPerService Comment - Maximum number of simultaneous curl connections per host + Maximum number of simultaneous curl connections per host:port service Persist 0 Type U32 Value - 16 + 8 CurlTimeoutDNSLookup @@ -4965,7 +4935,18 @@ This should be as low as possible, but too low may break functionality Value -1 - DebugStatModeTexture + DebugStatModeHTTPTexture + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeUDPTexture Comment Mode of stat in Statistics floater diff --git a/indra/newview/app_settings/settings_crash_behavior.xml b/indra/newview/app_settings/settings_crash_behavior.xml index f588db5cb..433583bee 100644 --- a/indra/newview/app_settings/settings_crash_behavior.xml +++ b/indra/newview/app_settings/settings_crash_behavior.xml @@ -1,15 +1,48 @@ - + CrashSubmitBehavior - - Comment - Controls behavior when viewer crashes (0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report) - Persist - 2 - Type - S32 - Value - 2 - + + Comment + Controls behavior when viewer crashes (0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report) + Persist + 2 + Type + S32 + Value + 2 + CurlMaxTotalConcurrentConnections + + Comment + Maximum total number of simultaneous curl connections + Persist + 1 + Type + U32 + Value + 64 + + CurlConcurrentConnectionsPerService + + Comment + Maximum number of simultaneous curl connections per host:port service + Persist + 0 + Type + U32 + Value + 16 + + NoVerifySSLCert + + Comment + Do not verify SSL certificates. + Persist + 1 + Type + Boolean + Value + 1 + + diff --git a/indra/newview/generichandlers.cpp b/indra/newview/generichandlers.cpp new file mode 100644 index 000000000..8c1a9fc42 --- /dev/null +++ b/indra/newview/generichandlers.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) The Singularity dev Team and Melanie Thielker + * Refer to the singularity project for a full lst of copyright holders + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Neither the name of the Singularity Viewwer Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "llviewerprecompiledheaders.h" +#include "generichandlers.h" +#include "llviewergenericmessage.h" +#include "llviewerinventory.h" +#include "llappearancemgr.h" +#include "llviewerregion.h" +#include "llagent.h" + +GenericHandlers *gGenericHandlers = NULL; + +class DispatchReplaceOutfit : public LLDispatchHandler +{ +public: + virtual bool operator()( + const LLDispatcher* dispatcher, + const std::string& key, + const LLUUID& invoice, + const sparam_t& strings) + { + if (strings.size() != 1) return false; + LLUUID folder_id(strings[0]); + + bool success = false; + LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); + if (cat != NULL) + { + LLAppearanceMgr::instance().wearInventoryCategory(cat, FALSE, FALSE); + success = true; + } + + LLViewerRegion* regionp = gAgent.getRegion(); + if (!regionp) return true; + + std::string url = regionp->getCapability("WearablesLoaded"); + if (url.empty()) return true; + + LLSD data = LLSD(success); + + LLHTTPClient::post(url, data, new LLHTTPClient::ResponderIgnore); + + return true; + } +}; + +static DispatchReplaceOutfit sDispatchReplaceOutfit; + +GenericHandlers::GenericHandlers() +{ + gGenericDispatcher.addHandler("replaceoutfit", &sDispatchReplaceOutfit); +} + diff --git a/indra/newview/generichandlers.h b/indra/newview/generichandlers.h new file mode 100644 index 000000000..2a1210b57 --- /dev/null +++ b/indra/newview/generichandlers.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) The Singularity dev Team and Melanie Thielker + * Refer to the singularity project for a full lst of copyright holders + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Neither the name of the Singularity Viewwer Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +class GenericHandlers +{ +public: + GenericHandlers(); +}; + +extern GenericHandlers *gGenericHandlers; diff --git a/indra/newview/hippofloaterxml.cpp b/indra/newview/hippofloaterxml.cpp index 1d132a0eb..76aa9abcf 100644 --- a/indra/newview/hippofloaterxml.cpp +++ b/indra/newview/hippofloaterxml.cpp @@ -64,6 +64,8 @@ class HippoFloaterXmlImpl : public LLFloater const std::string &cmds, std::string::size_type &offset, std::string &response); + U32 mChannel; + private: std::string mName; bool mIsNotifyOnClose; @@ -162,7 +164,7 @@ void XmlData::release(const std::string &floaterName) // create HippoFloaterXmlImpl HippoFloaterXmlImpl::HippoFloaterXmlImpl(const std::string &name, const std::string &xml) : - mName(name), mIsNotifyOnClose(false) + mName(name), mIsNotifyOnClose(false), mChannel(CHANNEL) { gInstances[mName] = this; LLUICtrlFactory::getInstance()->buildFloaterFromBuffer(this, xml); @@ -223,7 +225,7 @@ void HippoFloaterXml::execute(const std::string &cmds) if (!floater->execute(floater, floater, cmds, offset, response)) break; if (!response.empty()) - send_chat_from_viewer(response, CHAT_TYPE_WHISPER, CHANNEL); + send_chat_from_viewer(response, CHAT_TYPE_WHISPER, floater->mChannel); } } else @@ -267,18 +269,22 @@ void HippoFloaterXml::execute(const std::string &cmds) // ******************************************************************** // generic notification callbacks -static void notifyCallback(void *c) +static void notifyCallback(void *c, void *f) { + HippoFloaterXmlImpl* floaterp = (HippoFloaterXmlImpl*)f; + LLUICtrl *ctrl = (LLUICtrl *)c; std::string msg = "NOTIFY:"; msg += ctrl->getName(); msg += ':'; msg += ctrl->getValue().asString(); - send_chat_from_viewer(msg, CHAT_TYPE_WHISPER, CHANNEL); + send_chat_from_viewer(msg, CHAT_TYPE_WHISPER, floaterp->mChannel); } -void callbackAvatarPick(void *c, const uuid_vec_t& ids, const std::vector& names) +void callbackAvatarPick(void *c, void *f, const uuid_vec_t& ids, const std::vector& names) { + HippoFloaterXmlImpl* floaterp = (HippoFloaterXmlImpl*)f; + LLUICtrl *ctrl = (LLUICtrl *)c; LLUUID id = ids[0]; @@ -289,7 +295,7 @@ void callbackAvatarPick(void *c, const uuid_vec_t& ids, const std::vectormChannel); } static void pickerCallback(void *c, void *f) @@ -297,7 +303,7 @@ static void pickerCallback(void *c, void *f) LLUICtrl *ctrl = (LLUICtrl *)c; HippoFloaterXmlImpl* floaterp = (HippoFloaterXmlImpl*)f; - floaterp->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&callbackAvatarPick, ctrl, _1, _2), FALSE, TRUE)); + floaterp->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&callbackAvatarPick, ctrl, floaterp, _1, _2), FALSE, TRUE)); //send_chat_from_viewer(msg, CHAT_TYPE_WHISPER, CHANNEL); } @@ -305,7 +311,7 @@ void HippoFloaterXmlImpl::onClose(bool quitting) { if (mIsNotifyOnClose) send_chat_from_viewer("NOTIFY:" + mName + ":closed", - CHAT_TYPE_WHISPER, CHANNEL); + CHAT_TYPE_WHISPER, mChannel); LLFloater::onClose(quitting); } @@ -342,17 +348,23 @@ bool HippoFloaterXmlImpl::execute(LLFloater *floater, LLUICtrl *ctrl, return false; } else if (key == "setValue") { ctrl->setValue(value); + } else if (key == "channel") { + if (HippoFloaterXmlImpl *floater = dynamic_cast(ctrl)) { + floater->mChannel = atoi(value.c_str()); + } } else if (key == "setLabel") { /*ctrl->setLabel(value);*/ } else if (key == "setVisible") { ctrl->setVisible(value != "0"); + } else if (key == "setEnabled") { + ctrl->setEnabled(value != "0"); } else if (key == "notify") { bool set = (value != "0"); - if (HippoFloaterXmlImpl *floater = dynamic_cast(ctrl)) { - floater->mIsNotifyOnClose = set; + if (HippoFloaterXmlImpl *floaterp = dynamic_cast(ctrl)) { + floaterp->mIsNotifyOnClose = set; } else { if (set) - ctrl->setCommitCallback(boost::bind(¬ifyCallback, _1), ctrl); + ctrl->setCommitCallback(boost::bind(¬ifyCallback, _1, floater), ctrl); else ctrl->setCommitCallback(0); } diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index bab5eb8dd..93168da88 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -52,13 +52,16 @@ HippoGridInfo::HippoGridInfo(const std::string& gridName) : mXmlState(XML_VOID), mVoiceConnector("SLVoice"), mIsInProductionGrid(false), + mIsInAvination(false), mRenderCompat(true), mInvLinks(false), mAutoUpdate(false), mMaxAgentGroups(-1), mCurrencySymbol("OS$"), + mCurrencyText("OS Dollars"), mRealCurrencySymbol("US$"), - mDirectoryFee(30) + mDirectoryFee(30), + mUPCSupported(false) { } @@ -66,37 +69,6 @@ HippoGridInfo::HippoGridInfo(const std::string& gridName) : // ******************************************************************** // Getters -HippoGridInfo::Platform HippoGridInfo::getPlatform() -{ - return mPlatform; -} - -bool HippoGridInfo::isOpenSimulator() const -{ - return (mPlatform == HippoGridInfo::PLATFORM_OPENSIM || mPlatform == HippoGridInfo::PLATFORM_AURORA); -} - -bool HippoGridInfo::isAurora() const -{ - return (mPlatform == HippoGridInfo::PLATFORM_AURORA); -} - -bool HippoGridInfo::isSecondLife() const -{ - return (mPlatform == HippoGridInfo::PLATFORM_SECONDLIFE); -} - -bool HippoGridInfo::isInProductionGrid() const -{ - llassert(mPlatform == HippoGridInfo::PLATFORM_SECONDLIFE); - return mIsInProductionGrid; -} - -const std::string& HippoGridInfo::getGridName() const -{ - return mGridName; -} - const std::string& HippoGridInfo::getGridOwner() const { if(isSecondLife()) @@ -110,68 +82,6 @@ const std::string& HippoGridInfo::getGridOwner() const } } -const std::string& HippoGridInfo::getLoginUri() const -{ - return mLoginUri; -} - -const std::string& HippoGridInfo::getLoginPage() const -{ - return mLoginPage; -} - -const std::string& HippoGridInfo::getHelperUri() const -{ - return mHelperUri; -} - -const std::string& HippoGridInfo::getWebSite() const -{ - return mWebSite; -} - -const std::string& HippoGridInfo::getSupportUrl() const -{ - return mSupportUrl; -} - -const std::string& HippoGridInfo::getRegisterUrl() const -{ - return mRegisterUrl; -} - -const std::string& HippoGridInfo::getPasswordUrl() const -{ - return mPasswordUrl; -} - -const std::string& HippoGridInfo::getSearchUrl() const -{ - return mSearchUrl; -} - -const std::string& HippoGridInfo::getGridMessage() const -{ - return mGridMessage; -} - -bool HippoGridInfo::isRenderCompat() const -{ - return mRenderCompat; -} - -const std::string& HippoGridInfo::getCurrencySymbol() const -{ - return mCurrencySymbol; -} - -const std::string& HippoGridInfo::getRealCurrencySymbol() const -{ - return mRealCurrencySymbol; -} - - - // ******************************************************************** // Setters @@ -181,6 +91,7 @@ void HippoGridInfo::setPlatform(Platform platform) if (mPlatform == PLATFORM_SECONDLIFE) { mCurrencySymbol = "L$"; + mCurrencyText = "Linden Dollars"; } } @@ -240,6 +151,10 @@ void HippoGridInfo::setGridNick(std::string gridNick) { mIsInProductionGrid = true; } + if(gridNick == "avination") + { + mIsInAvination = true; + } } void HippoGridInfo::setLoginUri(const std::string& loginUri) @@ -250,6 +165,11 @@ void HippoGridInfo::setLoginUri(const std::string& loginUri) { mIsInProductionGrid = true; } + if (utf8str_tolower(LLURI(uri).hostName()) == "login.avination.com" || + utf8str_tolower(LLURI(uri).hostName()) == "login.avination.net") + { + mIsInAvination = true; + } } void HippoGridInfo::setLoginPage(const std::string& loginPage) @@ -303,6 +223,11 @@ void HippoGridInfo::setCurrencySymbol(const std::string& sym) mCurrencySymbol = sym.substr(0, 3); } +void HippoGridInfo::setCurrencyText(const std::string& text) +{ + mCurrencyText = text; +} + void HippoGridInfo::setRealCurrencySymbol(const std::string& sym) { mRealCurrencySymbol = sym.substr(0, 3); @@ -456,8 +381,11 @@ void HippoGridInfo::onXmlCharacterData(void* userData, const XML_Char* s, int le { case XML_GRIDNICK: { - if (self->mGridNick == "") self->mGridNick.assign(s, len); - self->mGridNick = sanitizeGridNick(self->mGridNick); + if (self->mGridNick == "") + { + self->mGridNick.assign(s, len); + self->mGridNick = sanitizeGridNick(self->mGridNick); + } break; } @@ -489,7 +417,15 @@ void HippoGridInfo::onXmlCharacterData(void* userData, const XML_Char* s, int le break; } - case XML_GRIDNAME: self->mGridName.assign(s, len); break; + case XML_GRIDNAME: + { + if (self->mGridName == "") + { + self->mGridName.assign(s, len); + } + break; + } + case XML_LOGINPAGE: self->mLoginPage.assign(s, len); break; case XML_WEBSITE: self->mWebSite.assign(s, len); break; case XML_SUPPORT: self->mSupportUrl.assign(s, len); break; @@ -676,6 +612,19 @@ void HippoGridInfo::setAutoUpdate(bool b) mAutoUpdate = b; } +bool HippoGridInfo::getUPCSupported() +{ + if(isSecondLife()) + return false; + else + return mUPCSupported; +} + +void HippoGridInfo::setUPCSupported(bool b) +{ + mUPCSupported = b; +} + // ******************************************************************** // ******************************************************************** // HippoGridManager @@ -747,13 +696,6 @@ HippoGridInfo* HippoGridManager::getGrid(const std::string& grid) const } } - -HippoGridInfo* HippoGridManager::getConnectedGrid() const -{ - return (mConnectedGrid)? mConnectedGrid: getCurrentGrid(); -} - - HippoGridInfo* HippoGridManager::getCurrentGrid() const { HippoGridInfo* grid = getGrid(mCurrentGrid); diff --git a/indra/newview/hippogridmanager.h b/indra/newview/hippogridmanager.h index 310033af7..9562d563f 100644 --- a/indra/newview/hippogridmanager.h +++ b/indra/newview/hippogridmanager.h @@ -36,31 +36,33 @@ public: explicit HippoGridInfo(const std::string& gridName); - Platform getPlatform(); - bool isOpenSimulator() const; - bool isAurora() const; - bool isSecondLife() const; - bool isInProductionGrid() const; // Should only be called if isSecondLife() returns true. - const std::string& getGridName() const; + Platform getPlatform() { return mPlatform; } + bool isOpenSimulator() const { return (mPlatform == PLATFORM_OPENSIM || mPlatform == PLATFORM_AURORA); } + bool isAurora() const { return (mPlatform == PLATFORM_AURORA); } + bool isSecondLife() const { return (mPlatform == PLATFORM_SECONDLIFE); } + bool isAvination() const { return mIsInAvination; } + bool isInProductionGrid() const { llassert(mPlatform == PLATFORM_SECONDLIFE); return mIsInProductionGrid; } // Should only be called if isSecondLife() returns true. + const std::string& getGridName() const { return mGridName; } const std::string& getGridOwner() const; - const std::string& getLoginUri() const; - const std::string& getLoginPage() const; - const std::string& getHelperUri() const; - const std::string& getWebSite() const; - const std::string& getSupportUrl() const; - const std::string& getRegisterUrl() const; - const std::string& getPasswordUrl() const; + const std::string& getLoginUri() const { return mLoginUri; } + const std::string& getLoginPage() const { return mLoginPage; } + const std::string& getHelperUri() const { return mHelperUri; } + const std::string& getWebSite() const { return mWebSite; } + const std::string& getSupportUrl() const { return mSupportUrl; } + const std::string& getRegisterUrl() const { return mRegisterUrl; } + const std::string& getPasswordUrl() const { return mPasswordUrl; } // Returns the url base used for the Web Search tab - const std::string& getSearchUrl() const; - const std::string& getGridMessage() const; + const std::string& getSearchUrl() const { return mSearchUrl; } + const std::string& getGridMessage() const { return mGridMessage; } const std::string& getVoiceConnector() const { return mVoiceConnector; } std::string getSearchUrl(SearchType ty, bool is_web) const; - bool isRenderCompat() const; - std::string getGridNick(); + bool isRenderCompat() const { return mRenderCompat; } + std::string getGridNick(); int getMaxAgentGroups() const { return mMaxAgentGroups; } - const std::string& getCurrencySymbol() const; - const std::string& getRealCurrencySymbol() const; + const std::string& getCurrencySymbol() const { return mCurrencySymbol; } + const std::string& getCurrencyText() const { return mCurrencyText; } + const std::string& getRealCurrencySymbol() const { return mRealCurrencySymbol; } std::string getUploadFee() const; std::string getGroupCreationFee() const; std::string getDirectoryFee() const; @@ -82,8 +84,11 @@ public: void setRenderCompat(bool compat); void setMaxAgentGroups(int max) { mMaxAgentGroups = max; } void setVoiceConnector(const std::string& vc) { mVoiceConnector = vc; } + void setUPCSupported(bool on); + bool getUPCSupported(); void setCurrencySymbol(const std::string& sym); + void setCurrencyText(const std::string& text); void setRealCurrencySymbol(const std::string& sym); void setDirectoryFee(int fee); bool supportsInvLinks(); @@ -113,12 +118,15 @@ private: std::string mSearchUrl; std::string mVoiceConnector; bool mIsInProductionGrid; + bool mIsInAvination; bool mRenderCompat; bool mInvLinks; bool mAutoUpdate; + bool mUPCSupported; int mMaxAgentGroups; std::string mCurrencySymbol; + std::string mCurrencyText; std::string mRealCurrencySymbol; int mDirectoryFee; std::string mGridMessage; @@ -152,7 +160,8 @@ public: void discardAndReload(); HippoGridInfo* getGrid(const std::string& grid) const; - HippoGridInfo* getConnectedGrid() const; + HippoGridInfo* getConnectedGrid() const { return mConnectedGrid ? mConnectedGrid : getCurrentGrid(); } + HippoGridInfo* getCurrentGrid() const; const std::string& getDefaultGridNick() const; const std::string& getCurrentGridNick() const; diff --git a/indra/newview/hippopanelgrids.cpp b/indra/newview/hippopanelgrids.cpp index 61ebcdde2..6af943c5b 100644 --- a/indra/newview/hippopanelgrids.cpp +++ b/indra/newview/hippopanelgrids.cpp @@ -133,7 +133,7 @@ BOOL HippoPanelGridsImpl::postBuild() requires("btn_add"); requires("btn_copy"); requires("btn_default"); - //requires("btn_gridinfo"); + requires("btn_gridinfo"); requires("btn_help_render_compat"); if (!checkRequirements()) return false; @@ -146,7 +146,7 @@ BOOL HippoPanelGridsImpl::postBuild() childSetAction("btn_add", onClickAdd, this); childSetAction("btn_copy", onClickCopy, this); childSetAction("btn_default", onClickDefault, this); - //childSetAction("btn_gridinfo", onClickGridInfo, this); + childSetAction("btn_gridinfo", onClickGridInfo, this); childSetAction("btn_help_render_compat", onClickHelpRenderCompat, this); childSetAction("btn_advanced", onClickAdvanced, this); diff --git a/indra/newview/linux_tools/refresh_desktop_app_entry.sh b/indra/newview/linux_tools/refresh_desktop_app_entry.sh old mode 100644 new mode 100755 diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh index af9d73189..d6a266240 100755 --- a/indra/newview/linux_tools/wrapper.sh +++ b/indra/newview/linux_tools/wrapper.sh @@ -141,7 +141,7 @@ if [ -n "$LL_TCMALLOC" ]; then fi export VIEWER_BINARY='singularity-do-not-run-directly' -BINARY_TYPE=$(expr match "$(file -b bin/$VIEWER_BINARY)" '\(.*executable\)') +BINARY_TYPE=$(expr match "$(file -b bin/$VIEWER_BINARY)" '\(.*executable\)' | sed -e 's/ / /g') if [ "${BINARY_TYPE}" == "ELF 64-bit LSB executable" ]; then SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib64:`pwd`/lib32:$LD_LIBRARY_PATH"' else diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp index a1c60e921..3b48606a5 100644 --- a/indra/newview/llagentwearablesfetch.cpp +++ b/indra/newview/llagentwearablesfetch.cpp @@ -135,7 +135,7 @@ void LLInitialWearablesFetch::processContents() LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t wearable_array; LLFindWearables is_wearable; - llassert_always(mComplete.size() != 0); + llassert(mComplete.size() != 0); gInventory.collectDescendentsIf(mComplete.front(), cat_array, wearable_array, LLInventoryModel::EXCLUDE_TRASH, is_wearable); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index d09e8526d..07ae74f6b 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1895,9 +1895,7 @@ bool LLAppViewer::initThreads() // State machine thread. startEngineThread(); - AICurlInterface::startCurlThread(gSavedSettings.getU32("CurlMaxTotalConcurrentConnections"), - gSavedSettings.getU32("CurlConcurrentConnectionsPerHost"), - gSavedSettings.getBOOL("NoVerifySSLCert")); + AICurlInterface::startCurlThread(&gSavedSettings); LLImage::initClass(); diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp index 3efc942da..75d3c4eda 100644 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -237,7 +237,7 @@ namespace if (!content.get("events") || !content.get("id")) { - llwarns << "received event poll with no events or id key" << llendl; + //llwarns << "received event poll with no events or id key" << llendl; makeRequest(); return; } diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp index 6aa573ba8..8fdfc1a93 100644 --- a/indra/newview/llfloateranimpreview.cpp +++ b/indra/newview/llfloateranimpreview.cpp @@ -2,9 +2,7 @@ * @file llfloateranimpreview.cpp * @brief LLFloaterAnimPreview class implementation * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. + * Copyright (c) 2012, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -31,1600 +29,18 @@ */ #include "llviewerprecompiledheaders.h" - #include "llfloateranimpreview.h" -#include "llbvhloader.h" -#include "lldatapacker.h" -#include "lldir.h" -#include "llnotificationsutil.h" -#include "llvfile.h" -#include "llapr.h" -#include "llstring.h" - -#include "llagent.h" -#include "llanimationstates.h" -#include "llbbox.h" -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "llcombobox.h" -#include "lldrawable.h" -#include "lldrawpoolavatar.h" -#include "llrender.h" -#include "llface.h" -#include "llfocusmgr.h" -#include "llkeyframemotion.h" -#include "lllineeditor.h" -#include "llfloaterperms.h" -#include "llsliderctrl.h" -#include "llspinctrl.h" -#include "lltextbox.h" -#include "lltoolmgr.h" -#include "llui.h" -#include "llviewercamera.h" -#include "llviewerobjectlist.h" -#include "llviewerwindow.h" -#include "llviewermenufile.h" // upload_new_resource() -#include "llvoavatarself.h" -#include "pipeline.h" -#include "lluictrlfactory.h" -#include "llviewercontrol.h" - -#include "hippogridmanager.h" - -// -#include "llinventorymodel.h" // gInventoryModel -// - -S32 LLFloaterAnimPreview::sUploadAmount = 10; - -const S32 PREVIEW_BORDER_WIDTH = 2; -const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH; -const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; -const S32 PREF_BUTTON_HEIGHT = 16; -const S32 PREVIEW_TEXTURE_HEIGHT = 300; - -const F32 PREVIEW_CAMERA_DISTANCE = 4.f; - -const F32 MIN_CAMERA_ZOOM = 0.5f; -const F32 MAX_CAMERA_ZOOM = 10.f; - -const F32 BASE_ANIM_TIME_OFFSET = 5.f; - -// -struct LLSaveInfo +LLFloaterAnimPreview::LLFloaterAnimPreview(LLSD const& filename) : LLFloaterNameDesc(filename) { - LLSaveInfo(const LLUUID& item_id, const LLUUID& object_id, const std::string& desc, - const LLTransactionID tid) - : mItemUUID(item_id), mObjectUUID(object_id), mDesc(desc), mTransactionID(tid) - { - } - - LLUUID mItemUUID; - LLUUID mObjectUUID; - std::string mDesc; - LLTransactionID mTransactionID; -}; -// - -std::string STATUS[] = -{ - "E_ST_OK", - "E_ST_EOF", - "E_ST_NO_CONSTRAINT", - "E_ST_NO_FILE", - "E_ST_NO_HIER", - "E_ST_NO_JOINT", - "E_ST_NO_NAME", - "E_ST_NO_OFFSET", - "E_ST_NO_CHANNELS", - "E_ST_NO_ROTATION", - "E_ST_NO_AXIS", - "E_ST_NO_MOTION", - "E_ST_NO_FRAMES", - "E_ST_NO_FRAME_TIME", - "E_ST_NO_POS", - "E_ST_NO_ROT", - "E_ST_NO_XLT_FILE", - "E_ST_NO_XLT_HEADER", - "E_ST_NO_XLT_NAME", - "E_ST_NO_XLT_IGNORE", - "E_ST_NO_XLT_RELATIVE", - "E_ST_NO_XLT_OUTNAME", - "E_ST_NO_XLT_MATRIX", - "E_ST_NO_XLT_MERGECHILD", - "E_ST_NO_XLT_MERGEPARENT", - "E_ST_NO_XLT_PRIORITY", - "E_ST_NO_XLT_LOOP", - "E_ST_NO_XLT_EASEIN", - "E_ST_NO_XLT_EASEOUT", - "E_ST_NO_XLT_HAND", - "E_ST_NO_XLT_EMOTE", -"E_ST_BAD_ROOT" -}; - - -//----------------------------------------------------------------------------- -// LLFloaterAnimPreview() -//----------------------------------------------------------------------------- -LLFloaterAnimPreview::LLFloaterAnimPreview(const std::string& filename, void* item) : - LLFloaterNameDesc(filename) -{ - // - mItem = item; - // - - mLastMouseX = 0; - mLastMouseY = 0; - - mIDList["Standing"] = ANIM_AGENT_STAND; - mIDList["Walking"] = ANIM_AGENT_FEMALE_WALK; - mIDList["Sitting"] = ANIM_AGENT_SIT_FEMALE; - mIDList["Flying"] = ANIM_AGENT_HOVER; - - mIDList["[None]"] = LLUUID::null; - mIDList["Aaaaah"] = ANIM_AGENT_EXPRESS_OPEN_MOUTH; - mIDList["Afraid"] = ANIM_AGENT_EXPRESS_AFRAID; - mIDList["Angry"] = ANIM_AGENT_EXPRESS_ANGER; - mIDList["Big Smile"] = ANIM_AGENT_EXPRESS_TOOTHSMILE; - mIDList["Bored"] = ANIM_AGENT_EXPRESS_BORED; - mIDList["Cry"] = ANIM_AGENT_EXPRESS_CRY; - mIDList["Disdain"] = ANIM_AGENT_EXPRESS_DISDAIN; - mIDList["Embarrassed"] = ANIM_AGENT_EXPRESS_EMBARRASSED; - mIDList["Frown"] = ANIM_AGENT_EXPRESS_FROWN; - mIDList["Kiss"] = ANIM_AGENT_EXPRESS_KISS; - mIDList["Laugh"] = ANIM_AGENT_EXPRESS_LAUGH; - mIDList["Plllppt"] = ANIM_AGENT_EXPRESS_TONGUE_OUT; - mIDList["Repulsed"] = ANIM_AGENT_EXPRESS_REPULSED; - mIDList["Sad"] = ANIM_AGENT_EXPRESS_SAD; - mIDList["Shrug"] = ANIM_AGENT_EXPRESS_SHRUG; - mIDList["Smile"] = ANIM_AGENT_EXPRESS_SMILE; - mIDList["Surprise"] = ANIM_AGENT_EXPRESS_SURPRISE; - mIDList["Wink"] = ANIM_AGENT_EXPRESS_WINK; - mIDList["Worry"] = ANIM_AGENT_EXPRESS_WORRY; } -//----------------------------------------------------------------------------- -// setAnimCallbacks() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::setAnimCallbacks() -{ - childSetCommitCallback("playback_slider", onSliderMove, this); - - childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); - childSetValue("preview_base_anim", "Standing"); - - childSetCommitCallback("priority", onCommitPriority, this); - childSetCommitCallback("loop_check", onCommitLoop, this); - childSetCommitCallback("loop_in_point", onCommitLoopIn, this); - childSetValidate("loop_in_point", validateLoopIn); - childSetCommitCallback("loop_out_point", onCommitLoopOut, this); - childSetValidate("loop_out_point", validateLoopOut); - - childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); - - childSetCommitCallback("emote_combo", onCommitEmote, this); - childSetValue("emote_combo", "[None]"); - - childSetCommitCallback("ease_in_time", onCommitEaseIn, this); - childSetValidate("ease_in_time", validateEaseIn); - childSetCommitCallback("ease_out_time", onCommitEaseOut, this); - childSetValidate("ease_out_time", validateEaseOut); -} - -//----------------------------------------------------------------------------- -// postBuild() -//----------------------------------------------------------------------------- BOOL LLFloaterAnimPreview::postBuild() { - LLRect r; - LLKeyframeMotion* motionp = NULL; - LLBVHLoader* loaderp = NULL; - if (!LLFloaterNameDesc::postBuild()) { return FALSE; } - - mInWorld = gSavedSettings.getBOOL("PreviewAnimInWorld"); - - childSetCommitCallback("name_form", onCommitName, this); - - if (gSavedSettings.getBOOL("AscentPowerfulWizard")) - { - childSetMaxValue("priority", 7); - } - - childSetLabelArg("ok_btn", "[UPLOADFEE]", gHippoGridManager->getConnectedGrid()->getUploadFee()); - childSetAction("ok_btn", onBtnOK, this); - setDefaultBtn(); - - if (mInWorld) - { - r = getRect(); - translate(0, 230); - reshape(r.getWidth(), r.getHeight() - 230); - childSetValue("bad_animation_text", getString("in_world")); - childShow("bad_animation_text"); - } - else - { - childHide("bad_animation_text"); - } - - mPreviewRect.set(PREVIEW_HPAD, - PREVIEW_TEXTURE_HEIGHT, - getRect().getWidth() - PREVIEW_HPAD, - PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); - mPreviewImageRect.set(0.f, 1.f, 1.f, 0.f); - - S32 y = mPreviewRect.mTop + BTN_HEIGHT; - S32 btn_left = PREVIEW_HPAD; - - r.set( btn_left, y, btn_left + 32, y - BTN_HEIGHT ); - mPlayButton = getChild( "play_btn"); - if (!mPlayButton) - { - mPlayButton = new LLButton(std::string("play_btn"), LLRect(0,0,0,0)); - } - mPlayButton->setClickedCallback(boost::bind(&LLFloaterAnimPreview::onBtnPlay,this)); - - mPlayButton->setImages(std::string("button_anim_play.tga"), - std::string("button_anim_play_selected.tga")); - - mPlayButton->setImageDisabled(NULL); - mPlayButton->setImageDisabledSelected(NULL); - - mPlayButton->setScaleImage(TRUE); - - mStopButton = getChild( "stop_btn"); - if (!mStopButton) - { - mStopButton = new LLButton(std::string("stop_btn"), LLRect(0,0,0,0)); - } - mStopButton->setClickedCallback(boost::bind(&LLFloaterAnimPreview::onBtnStop, this)); - - mStopButton->setImages(std::string("button_anim_stop.tga"), - std::string("button_anim_stop_selected.tga")); - - mStopButton->setImageDisabled(NULL); - mStopButton->setImageDisabledSelected(NULL); - - mStopButton->setScaleImage(TRUE); - - r.set(r.mRight + PREVIEW_HPAD, y, getRect().getWidth() - PREVIEW_HPAD, y - BTN_HEIGHT); - //childSetCommitCallback("playback_slider", onSliderMove, this); - - //childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); - //childSetValue("preview_base_anim", "Standing"); - - //childSetCommitCallback("priority", onCommitPriority, this); - //childSetCommitCallback("loop_check", onCommitLoop, this); - //childSetCommitCallback("loop_in_point", onCommitLoopIn, this); - //childSetValidate("loop_in_point", validateLoopIn); - //childSetCommitCallback("loop_out_point", onCommitLoopOut, this); - //childSetValidate("loop_out_point", validateLoopOut); - - //childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); - - //childSetCommitCallback("emote_combo", onCommitEmote, this); - //childSetValue("emote_combo", "[None]"); - - //childSetCommitCallback("ease_in_time", onCommitEaseIn, this); - //childSetValidate("ease_in_time", validateEaseIn); - //childSetCommitCallback("ease_out_time", onCommitEaseOut, this); - //childSetValidate("ease_out_time", validateEaseOut); - - // moved declaration from below - BOOL success = false; - // - - std::string exten = gDirUtilp->getExtension(mFilename); - if (exten == "bvh") - { - // loading a bvh file - - // now load bvh file - S32 file_size; - - LLAPRFile infile(mFilenameAndPath, LL_APR_RB, &file_size); - - if (!infile.getFileHandle()) - { - llwarns << "Can't open BVH file:" << mFilename << llendl; - } - else - { - char* file_buffer; - - file_buffer = new char[file_size + 1]; - - if (file_size == infile.read(file_buffer, file_size)) - { - file_buffer[file_size] = '\0'; - llinfos << "Loading BVH file " << mFilename << llendl; - ELoadStatus load_status = E_ST_OK; - S32 line_number = 0; - loaderp = new LLBVHLoader(file_buffer, load_status, line_number); - std::string status = getString(STATUS[load_status]); - - if(load_status == E_ST_NO_XLT_FILE) - { - llwarns << "NOTE: No translation table found." << llendl; - } - else - { - llwarns << "ERROR: [line: " << line_number << "] " << status << llendl; - } - } - - infile.close() ; - delete[] file_buffer; - - // moved everything bvh from below - if(loaderp && loaderp->isInitialized() && loaderp->getDuration() <= MAX_ANIM_DURATION) - { - mTransactionID.generate(); - mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); - - mAnimPreview = new LLPreviewAnimation(256, 256); - - // motion will be returned, but it will be in a load-pending state, as this is a new motion - // this motion will not request an asset transfer until next update, so we have a chance to - // load the keyframe data locally - if (mInWorld) - { - motionp = (LLKeyframeMotion*)gAgentAvatarp->createMotion(mMotionID); - } - else - { - motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID); - } - - // create data buffer for keyframe initialization - S32 buffer_size = loaderp->getOutputSize(); - U8* buffer = new U8[buffer_size]; - - LLDataPackerBinaryBuffer dp(buffer, buffer_size); - - // pass animation data through memory buffer - loaderp->serialize(dp); - dp.reset(); - success = motionp && motionp->deserialize(dp); - } - else - { - success = false; - if ( loaderp ) - { - if (loaderp->getDuration() > MAX_ANIM_DURATION) - { - LLUIString out_str = getString("anim_too_long"); - out_str.setArg("[LENGTH]", llformat("%.1f", loaderp->getDuration())); - out_str.setArg("[MAX_LENGTH]", llformat("%.1f", MAX_ANIM_DURATION)); - getChild("bad_animation_text")->setValue(out_str.getString()); - } - else - { - LLUIString out_str = getString("failed_file_read"); - out_str.setArg("[STATUS]", getString(STATUS[loaderp->getStatus()])); - getChild("bad_animation_text")->setValue(out_str.getString()); - } - } - - //setEnabled(FALSE); - mMotionID.setNull(); - mAnimPreview = NULL; - } - // - } - } - // - else if(exten == "animatn") - { - S32 file_size; - LLAPRFile raw_animatn(mFilenameAndPath, LL_APR_RB, &file_size); - - if (!raw_animatn.getFileHandle()) - { - llwarns << "Can't open animatn file:" << mFilename << llendl; - } - else - { - char* file_buffer; - - file_buffer = new char[file_size + 1]; - - if (file_size == raw_animatn.read(file_buffer, file_size)) - { - file_buffer[file_size] = '\0'; - llinfos << "Loading animatn file " << mFilename << llendl; - mTransactionID.generate(); - mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); - mAnimPreview = new LLPreviewAnimation(256, 256); - motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID); - LLDataPackerBinaryBuffer dp((U8*)file_buffer, file_size); - dp.reset(); - success = motionp && motionp->deserialize(dp); - } - - raw_animatn.close(); - delete[] file_buffer; - } - } - // - - if (success) - { - setAnimCallbacks() ; - - if (!mInWorld) - { - const LLBBoxLocal &pelvis_bbox = motionp->getPelvisBBox(); - - LLVector3 temp = pelvis_bbox.getCenter(); - // only consider XY? - //temp.mV[VZ] = 0.f; - F32 pelvis_offset = temp.magVec(); - - temp = pelvis_bbox.getExtent(); - //temp.mV[VZ] = 0.f; - F32 pelvis_max_displacement = pelvis_offset + (temp.magVec() * 0.5f) + 1.f; - - F32 camera_zoom = LLViewerCamera::getInstance()->getDefaultFOV() / (2.f * atan(pelvis_max_displacement / PREVIEW_CAMERA_DISTANCE)); - - mAnimPreview->setZoom(camera_zoom); - } - - motionp->setName(childGetValue("name_form").asString()); - if (!mInWorld) - { - mAnimPreview->getDummyAvatar()->startMotion(mMotionID); - } - childSetMinValue("playback_slider", 0.0); - childSetMaxValue("playback_slider", 1.0); - - childSetValue("loop_check", LLSD(motionp->getLoop())); - childSetValue("loop_in_point", LLSD(motionp->getLoopIn() / motionp->getDuration() * 100.f)); - childSetValue("loop_out_point", LLSD(motionp->getLoopOut() / motionp->getDuration() * 100.f)); - childSetValue("priority", LLSD((F32)motionp->getPriority())); - childSetValue("hand_pose_combo", LLHandMotion::getHandPoseName(motionp->getHandPose())); - childSetValue("ease_in_time", LLSD(motionp->getEaseInDuration())); - childSetValue("ease_out_time", LLSD(motionp->getEaseOutDuration())); - setEnabled(TRUE); - std::string seconds_string; - seconds_string = llformat(" - %.2f seconds", motionp->getDuration()); - - setTitle(mFilename + std::string(seconds_string)); - } - else - { - mAnimPreview = NULL; - mMotionID.setNull(); - childSetValue("bad_animation_text", getString("failed_to_initialize")); - } - - - refresh(); - - delete loaderp; - + getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this)); return TRUE; } - -//----------------------------------------------------------------------------- -// LLFloaterAnimPreview() -//----------------------------------------------------------------------------- -LLFloaterAnimPreview::~LLFloaterAnimPreview() -{ - if (mInWorld) - { - LLVOAvatar* avatarp = gAgentAvatarp; - if (avatarp) - { - if (mMotionID.notNull()) - { - avatarp->stopMotion(mMotionID, TRUE); - avatarp->removeMotion(mMotionID); - } - avatarp->deactivateAllMotions(); - avatarp->startMotion(ANIM_AGENT_HEAD_ROT); - avatarp->startMotion(ANIM_AGENT_EYE); - avatarp->startMotion(ANIM_AGENT_BODY_NOISE); - avatarp->startMotion(ANIM_AGENT_BREATHE_ROT); - avatarp->startMotion(ANIM_AGENT_HAND_MOTION); - avatarp->startMotion(ANIM_AGENT_PELVIS_FIX); - avatarp->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET); - } - } - mAnimPreview = NULL; - - setEnabled(FALSE); -} - -//----------------------------------------------------------------------------- -// draw() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::draw() -{ - LLFloater::draw(); - LLRect r = getRect(); - - refresh(); - - if (mMotionID.notNull() && mAnimPreview && !mInWorld) - { - gGL.color3f(1.f, 1.f, 1.f); - - gGL.getTexUnit(0)->bind(mAnimPreview); - - gGL.begin( LLRender::QUADS ); - { - gGL.texCoord2f(0.f, 1.f); - gGL.vertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT); - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); - gGL.texCoord2f(1.f, 0.f); - gGL.vertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); - gGL.texCoord2f(1.f, 1.f); - gGL.vertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT); - } - gGL.end(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar(); - if (!avatarp->areAnimationsPaused()) - { - mAnimPreview->requestUpdate(); - } - } -} - -//----------------------------------------------------------------------------- -// resetMotion() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::resetMotion() -{ - LLVOAvatar* avatarp; - if (mInWorld) - { - avatarp = gAgentAvatarp; - } - else - { - avatarp = mAnimPreview->getDummyAvatar(); - } - if (!avatarp) - { - return; - } - - BOOL paused = avatarp->areAnimationsPaused(); - - // *TODO: Fix awful casting hack - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); - - // Set emotion - std::string emote = childGetValue("emote_combo").asString(); - motionp->setEmote(mIDList[emote]); - - LLUUID base_id = mIDList[childGetValue("preview_base_anim").asString()]; - avatarp->deactivateAllMotions(); - avatarp->startMotion(base_id, BASE_ANIM_TIME_OFFSET); - avatarp->startMotion(mMotionID, 0.0f); - childSetValue("playback_slider", 0.0f); - - // Set pose - std::string handpose = childGetValue("hand_pose_combo").asString(); - avatarp->startMotion( ANIM_AGENT_HAND_MOTION, 0.0f ); - motionp->setHandPose(LLHandMotion::getHandPose(handpose)); - - if (paused) - { - mPauseRequest = avatarp->requestPause(); - } - else - { - mPauseRequest = NULL; - } -} - -//----------------------------------------------------------------------------- -// handleMouseDown() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (!mInWorld && mPreviewRect.pointInRect(x, y)) - { - bringToFront( x, y ); - gFocusMgr.setMouseCapture(this); - gViewerWindow->hideCursor(); - mLastMouseX = x; - mLastMouseY = y; - return TRUE; - } - - return LLFloater::handleMouseDown(x, y, mask); -} - -//----------------------------------------------------------------------------- -// handleMouseUp() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::handleMouseUp(S32 x, S32 y, MASK mask) -{ - if (!mInWorld) - { - gFocusMgr.setMouseCapture(FALSE); - gViewerWindow->showCursor(); - } - return LLFloater::handleMouseUp(x, y, mask); -} - -//----------------------------------------------------------------------------- -// handleHover() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::handleHover(S32 x, S32 y, MASK mask) -{ - if (mInWorld) - { - return TRUE; - } - - MASK local_mask = mask & ~MASK_ALT; - - if (mAnimPreview && hasMouseCapture()) - { - if (local_mask == MASK_PAN) - { - // pan here - mAnimPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f); - } - else if (local_mask == MASK_ORBIT) - { - F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; - F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f; - - mAnimPreview->rotate(yaw_radians, pitch_radians); - } - else - { - F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; - F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f; - - mAnimPreview->rotate(yaw_radians, 0.f); - mAnimPreview->zoom(zoom_amt); - } - - mAnimPreview->requestUpdate(); - - LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY); - } - - if (!mPreviewRect.pointInRect(x, y) || !mAnimPreview) - { - return LLFloater::handleHover(x, y, mask); - } - else if (local_mask == MASK_ORBIT) - { - gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA); - } - else if (local_mask == MASK_PAN) - { - gViewerWindow->setCursor(UI_CURSOR_TOOLPAN); - } - else - { - gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN); - } - - return TRUE; -} - -//----------------------------------------------------------------------------- -// handleScrollWheel() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - if (!mInWorld) - { - mAnimPreview->zoom((F32)clicks * -0.2f); - mAnimPreview->requestUpdate(); - } - return TRUE; -} - -//----------------------------------------------------------------------------- -// onMouseCaptureLost() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onMouseCaptureLost() -{ - if (!mInWorld) - { - gViewerWindow->showCursor(); - } -} - -//----------------------------------------------------------------------------- -// onBtnPlay() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onBtnPlay(void* user_data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data; - if (!previewp->getEnabled()) - return; - - if (previewp->mMotionID.notNull()) - { - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - - if(!avatarp->isMotionActive(previewp->mMotionID)) - { - previewp->resetMotion(); - previewp->mPauseRequest = NULL; - } - else - { - if (avatarp->areAnimationsPaused()) - { - previewp->mPauseRequest = NULL; - } - else - { - previewp->mPauseRequest = avatarp->requestPause(); - } - } - } -} - -//----------------------------------------------------------------------------- -// onBtnStop() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onBtnStop(void* user_data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data; - if (!previewp->getEnabled()) - return; - - if (previewp->mMotionID.notNull()) - { - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - previewp->resetMotion(); - previewp->mPauseRequest = avatarp->requestPause(); - } -} - -//----------------------------------------------------------------------------- -// onSliderMove() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onSliderMove(LLUICtrl* ctrl, void*user_data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - F32 slider_value = (F32)previewp->childGetValue("playback_slider").asReal(); - LLUUID base_id = previewp->mIDList[previewp->childGetValue("preview_base_anim").asString()]; - LLMotion* motionp = avatarp->findMotion(previewp->mMotionID); - F32 duration = motionp->getDuration();// + motionp->getEaseOutDuration(); - F32 delta_time = duration * slider_value; - avatarp->deactivateAllMotions(); - avatarp->startMotion(base_id, delta_time + BASE_ANIM_TIME_OFFSET); - avatarp->startMotion(previewp->mMotionID, delta_time); - previewp->mPauseRequest = avatarp->requestPause(); - previewp->refresh(); -} - -//----------------------------------------------------------------------------- -// onCommitBaseAnim() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitBaseAnim(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - - BOOL paused = avatarp->areAnimationsPaused(); - - // stop all other possible base motions - avatarp->stopMotion(ANIM_AGENT_STAND, TRUE); - avatarp->stopMotion(ANIM_AGENT_WALK, TRUE); - avatarp->stopMotion(ANIM_AGENT_SIT, TRUE); - avatarp->stopMotion(ANIM_AGENT_HOVER, TRUE); - - previewp->resetMotion(); - - if (!paused) - { - previewp->mPauseRequest = NULL; - } -} - -//----------------------------------------------------------------------------- -// onCommitLoop() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitLoop(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (motionp) - { - motionp->setLoop(previewp->childGetValue("loop_check").asBoolean()); - motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() * 0.01f * motionp->getDuration()); - motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); - } -} - -//----------------------------------------------------------------------------- -// onCommitLoopIn() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitLoopIn(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (motionp) - { - motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() / 100.f); - previewp->resetMotion(); - previewp->childSetValue("loop_check", LLSD(TRUE)); - onCommitLoop(ctrl, data); - } -} - -//----------------------------------------------------------------------------- -// onCommitLoopOut() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitLoopOut(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (motionp) - { - motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); - previewp->resetMotion(); - previewp->childSetValue("loop_check", LLSD(TRUE)); - onCommitLoop(ctrl, data); - } -} - -//----------------------------------------------------------------------------- -// onCommitName() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitName(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (motionp) - { - motionp->setName(previewp->childGetValue("name_form").asString()); - } - - LLFloaterNameDesc::doCommit(ctrl, data); -} - -//----------------------------------------------------------------------------- -// onCommitHandPose() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitHandPose(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - previewp->resetMotion(); // sets hand pose -} - -//----------------------------------------------------------------------------- -// onCommitEmote() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitEmote(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - previewp->resetMotion(); // ssts emote -} - -//----------------------------------------------------------------------------- -// onCommitPriority() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitPriority(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - motionp->setPriority(llfloor((F32)previewp->childGetValue("priority").asReal())); -} - -//----------------------------------------------------------------------------- -// onCommitEaseIn() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitEaseIn(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - motionp->setEaseIn((F32)previewp->childGetValue("ease_in_time").asReal()); - previewp->resetMotion(); -} - -//----------------------------------------------------------------------------- -// onCommitEaseOut() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitEaseOut(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - motionp->setEaseOut((F32)previewp->childGetValue("ease_out_time").asReal()); - previewp->resetMotion(); -} - -//----------------------------------------------------------------------------- -// validateEaseIn() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::validateEaseIn(LLUICtrl* spin, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return FALSE; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return FALSE; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return FALSE; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (!motionp->getLoop()) - { - F32 new_ease_in = llclamp((F32)previewp->childGetValue("ease_in_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseOutDuration()); - previewp->childSetValue("ease_in_time", LLSD(new_ease_in)); - } - - return TRUE; -} - -//----------------------------------------------------------------------------- -// validateEaseOut() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::validateEaseOut(LLUICtrl* spin, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - - if (!previewp->getEnabled()) - return FALSE; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return FALSE; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return FALSE; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (!motionp->getLoop()) - { - F32 new_ease_out = llclamp((F32)previewp->childGetValue("ease_out_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseInDuration()); - previewp->childSetValue("ease_out_time", LLSD(new_ease_out)); - } - - return TRUE; -} - -//----------------------------------------------------------------------------- -// validateLoopIn() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::validateLoopIn(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return FALSE; - - F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); - F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); - - if (loop_in_value < 0.f) - { - loop_in_value = 0.f; - } - else if (loop_in_value > 100.f) - { - loop_in_value = 100.f; - } - else if (loop_in_value > loop_out_value) - { - loop_in_value = loop_out_value; - } - - previewp->childSetValue("loop_in_point", LLSD(loop_in_value)); - return TRUE; -} - -//----------------------------------------------------------------------------- -// validateLoopOut() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::validateLoopOut(LLUICtrl* spin, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return FALSE; - - F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); - F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); - - if (loop_out_value < 0.f) - { - loop_out_value = 0.f; - } - else if (loop_out_value > 100.f) - { - loop_out_value = 100.f; - } - else if (loop_out_value < loop_in_value) - { - loop_out_value = loop_in_value; - } - - previewp->childSetValue("loop_out_point", LLSD(loop_out_value)); - return TRUE; -} - - -//----------------------------------------------------------------------------- -// refresh() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::refresh() -{ - if (!mAnimPreview) - { - childShow("bad_animation_text"); - mPlayButton->setEnabled(FALSE); - mStopButton->setEnabled(FALSE); - childDisable("ok_btn"); - } - else - { - if (!mInWorld) - { - childHide("bad_animation_text"); - } - mPlayButton->setEnabled(TRUE); - LLVOAvatar* avatarp; - if (mInWorld) - { - avatarp = gAgentAvatarp; - } - else - { - avatarp = mAnimPreview->getDummyAvatar(); - } - if (avatarp->isMotionActive(mMotionID)) - { - mStopButton->setEnabled(TRUE); - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); - if (avatarp->areAnimationsPaused()) - { - - mPlayButton->setImages(std::string("button_anim_play.tga"), - std::string("button_anim_play_selected.tga")); - - } - else - { - if (motionp) - { - F32 fraction_complete = motionp->getLastUpdateTime() / motionp->getDuration(); - childSetValue("playback_slider", fraction_complete); - } - mPlayButton->setImages(std::string("button_anim_pause.tga"), - std::string("button_anim_pause_selected.tga")); - - } - } - else - { - mPauseRequest = avatarp->requestPause(); - mPlayButton->setImages(std::string("button_anim_play.tga"), - std::string("button_anim_play_selected.tga")); - - mStopButton->setEnabled(TRUE); // stop also resets, leave enabled. - } - childEnable("ok_btn"); - if (!mInWorld) - { - mAnimPreview->requestUpdate(); - } - } -} - -//----------------------------------------------------------------------------- -// onBtnOK() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onBtnOK(void* userdata) -{ - LLFloaterAnimPreview* floaterp = (LLFloaterAnimPreview*)userdata; - if (!floaterp->getEnabled()) return; - - if ((!floaterp->mInWorld && floaterp->mAnimPreview) || (floaterp->mInWorld && gAgentAvatarp)) - { - LLKeyframeMotion* motionp; - if (floaterp->mInWorld) - { - motionp = (LLKeyframeMotion*)gAgentAvatarp->findMotion(floaterp->mMotionID); - } - else - { - motionp = (LLKeyframeMotion*)floaterp->mAnimPreview->getDummyAvatar()->findMotion(floaterp->mMotionID); - } - - S32 file_size = motionp->getFileSize(); - U8* buffer = new U8[file_size]; - - LLDataPackerBinaryBuffer dp(buffer, file_size); - if (motionp->serialize(dp)) - { - LLVFile file(gVFS, motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND); - - S32 size = dp.getCurrentSize(); - file.setMaxSize(size); - if (file.write((U8*)buffer, size)) - { - std::string name = floaterp->childGetValue("name_form").asString(); - std::string desc = floaterp->childGetValue("description_form").asString(); - LLAssetStorage::LLStoreAssetCallback callback = NULL; - S32 expected_upload_cost = sUploadAmount; - void *userdata = NULL; - - // - if(floaterp->mItem) - { - // Update existing item instead of creating a new one - LLViewerInventoryItem* item = (LLViewerInventoryItem*)floaterp->mItem; - LLSaveInfo* info = new LLSaveInfo(item->getUUID(), LLUUID::null, desc, floaterp->mTransactionID); - gAssetStorage->storeAssetData(floaterp->mTransactionID, LLAssetType::AT_ANIMATION, NULL, info, FALSE); - - // I guess I will do this now because the floater is closing... - LLPointer new_item = new LLViewerInventoryItem(item); - new_item->setDescription(desc); - new_item->setTransactionID(floaterp->mTransactionID); - new_item->setAssetUUID(motionp->getID()); - new_item->updateServer(FALSE); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); - } - else - // - { - upload_new_resource(floaterp->mTransactionID, // tid - LLAssetType::AT_ANIMATION, - name, - desc, - 0, - LLFolderType::FT_NONE, - LLInventoryType::IT_ANIMATION, - LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(), - name, - callback, expected_upload_cost, userdata); - } - } - else - { - llwarns << "Failure writing animation data." << llendl; - LLNotificationsUtil::add("WriteAnimationFail"); - } - } - - delete [] buffer; - // clear out cache for motion data - if (floaterp->mInWorld) - { - gAgentAvatarp->removeMotion(floaterp->mMotionID); - gAgentAvatarp->deactivateAllMotions(); - } - else - { - floaterp->mAnimPreview->getDummyAvatar()->removeMotion(floaterp->mMotionID); - } - LLKeyframeDataCache::removeKeyframeData(floaterp->mMotionID); - } - - floaterp->close(false); -} - -//----------------------------------------------------------------------------- -// LLPreviewAnimation -//----------------------------------------------------------------------------- -LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE) -{ - mNeedsUpdate = TRUE; - mCameraDistance = PREVIEW_CAMERA_DISTANCE; - mCameraYaw = 0.f; - mCameraPitch = 0.f; - mCameraZoom = 1.f; - - mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion()); - mDummyAvatar->createDrawable(&gPipeline); - mDummyAvatar->mIsDummy = TRUE; - mDummyAvatar->mSpecialRenderMode = 1; - mDummyAvatar->setPositionAgent(LLVector3::zero); - mDummyAvatar->slamPosition(); - mDummyAvatar->updateJointLODs(); - mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable); - mDummyAvatar->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET); - mDummyAvatar->hideSkirt(); - //gPipeline.markVisible(mDummyAvatar->mDrawable, *LLViewerCamera::getInstance()); - - // stop extraneous animations - mDummyAvatar->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE ); - mDummyAvatar->stopMotion( ANIM_AGENT_EYE, TRUE ); - mDummyAvatar->stopMotion( ANIM_AGENT_BODY_NOISE, TRUE ); - mDummyAvatar->stopMotion( ANIM_AGENT_BREATHE_ROT, TRUE ); -} - -//----------------------------------------------------------------------------- -// LLPreviewAnimation() -//----------------------------------------------------------------------------- -LLPreviewAnimation::~LLPreviewAnimation() -{ - mDummyAvatar->markDead(); -} - -//virtual -S8 LLPreviewAnimation::getType() const -{ - return LLViewerDynamicTexture::LL_PREVIEW_ANIMATION ; -} - -//----------------------------------------------------------------------------- -// update() -//----------------------------------------------------------------------------- -BOOL LLPreviewAnimation::render() -{ - mNeedsUpdate = FALSE; - LLVOAvatar* avatarp = mDummyAvatar; - - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.ortho(0.0f, mFullWidth, 0.0f, mFullHeight, -1.0f, 1.0f); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.pushMatrix(); - gGL.loadIdentity(); - - if (LLGLSLShader::sNoFixedFunction) - { - gUIProgram.bind(); - } - - LLGLSUIDefault def; - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4f(0.15f, 0.2f, 0.3f, 1.f); - - gl_rect_2d_simple( mFullWidth, mFullHeight ); - - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.popMatrix(); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); - - gGL.flush(); - - LLVector3 target_pos = avatarp->mRoot->getWorldPosition(); - - LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * - LLQuaternion(mCameraYaw, LLVector3::z_axis); - - LLQuaternion av_rot = avatarp->mRoot->getWorldRotation() * camera_rot; - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + mCameraOffset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos + (mCameraOffset * av_rot) ); // point of interest - - LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); - LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, FALSE); - - mCameraRelPos = LLViewerCamera::getInstance()->getOrigin() - avatarp->mHeadp->getWorldPosition(); - - //avatarp->setAnimationData("LookAtPoint", (void *)&mCameraRelPos); - - //SJB: Animation is updated in LLVOAvatar::updateCharacter - - if (avatarp->mDrawable.notNull()) - { - avatarp->updateLOD(); - - LLVertexBuffer::unbind(); - LLGLDepthTest gls_depth(GL_TRUE); - - LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool(); - avatarp->dirtyMesh(); - avatarPoolp->renderAvatars(avatarp); // renders only one avatar - } - - gGL.color4f(1,1,1,1); - return TRUE; -} - -//----------------------------------------------------------------------------- -// requestUpdate() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::requestUpdate() -{ - mNeedsUpdate = TRUE; -} - -//----------------------------------------------------------------------------- -// rotate() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::rotate(F32 yaw_radians, F32 pitch_radians) -{ - mCameraYaw = mCameraYaw + yaw_radians; - - mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); -} - -//----------------------------------------------------------------------------- -// zoom() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::zoom(F32 zoom_delta) -{ - setZoom(mCameraZoom + zoom_delta); -} - -//----------------------------------------------------------------------------- -// setZoom() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::setZoom(F32 zoom_amt) -{ - mCameraZoom = llclamp(zoom_amt, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM); -} - -//----------------------------------------------------------------------------- -// pan() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::pan(F32 right, F32 up) -{ - mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f); - mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f); -} - - - diff --git a/indra/newview/llfloateranimpreview.h b/indra/newview/llfloateranimpreview.h index 6390fe161..7603286e7 100644 --- a/indra/newview/llfloateranimpreview.h +++ b/indra/newview/llfloateranimpreview.h @@ -2,9 +2,7 @@ * @file llfloateranimpreview.h * @brief LLFloaterAnimPreview class definition * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. + * Copyright (c) 2012, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -34,112 +32,11 @@ #define LL_LLFLOATERANIMPREVIEW_H #include "llfloaternamedesc.h" -#include "lldynamictexture.h" -#include "llcharacter.h" -#include "llquaternion.h" -class LLVOAvatar; -class LLViewerJointMesh; - -class LLPreviewAnimation : public LLViewerDynamicTexture -{ -protected: - virtual ~LLPreviewAnimation(); - -public: - LLPreviewAnimation(S32 width, S32 height); - - /*virtual*/ S8 getType() const ; - - BOOL render(); - void requestUpdate(); - void rotate(F32 yaw_radians, F32 pitch_radians); - void zoom(F32 zoom_delta); - void setZoom(F32 zoom_amt); - void pan(F32 right, F32 up); - virtual BOOL needsUpdate() { return mNeedsUpdate; } - - LLVOAvatar* getDummyAvatar() { return mDummyAvatar; } - -protected: - BOOL mNeedsUpdate; - F32 mCameraDistance; - F32 mCameraYaw; - F32 mCameraPitch; - F32 mCameraZoom; - LLVector3 mCameraOffset; - LLVector3 mCameraRelPos; - LLPointer mDummyAvatar; -}; - -class LLFloaterAnimPreview : public LLFloaterNameDesc -{ -public: - // - LLFloaterAnimPreview(const std::string& filename, void* item = NULL); - // - virtual ~LLFloaterAnimPreview(); - - BOOL postBuild(); - - BOOL handleMouseDown(S32 x, S32 y, MASK mask); - BOOL handleMouseUp(S32 x, S32 y, MASK mask); - BOOL handleHover(S32 x, S32 y, MASK mask); - BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - void onMouseCaptureLost(); - - void refresh(); - - static void onBtnPlay(void*); - static void onBtnStop(void*); - static void setUploadAmount(S32 amount) { sUploadAmount = amount; } - static void onSliderMove(LLUICtrl*, void*); - static void onCommitBaseAnim(LLUICtrl*, void*); - static void onCommitLoop(LLUICtrl*, void*); - static void onCommitLoopIn(LLUICtrl*, void*); - static void onCommitLoopOut(LLUICtrl*, void*); - static BOOL validateLoopIn(LLUICtrl*, void*); - static BOOL validateLoopOut(LLUICtrl*, void*); - static void onCommitName(LLUICtrl*, void*); - static void onCommitHandPose(LLUICtrl*, void*); - static void onCommitEmote(LLUICtrl*, void*); - static void onCommitPriority(LLUICtrl*, void*); - static void onCommitEaseIn(LLUICtrl*, void*); - static void onCommitEaseOut(LLUICtrl*, void*); - static BOOL validateEaseIn(LLUICtrl*, void*); - static BOOL validateEaseOut(LLUICtrl*, void*); - static void onBtnOK(void*); - static void onSaveComplete(const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, - S32 status, LLExtStat ext_status); -private: - void setAnimCallbacks() ; - -protected: - void draw(); - void resetMotion(); - - LLPointer< LLPreviewAnimation> mAnimPreview; - S32 mLastMouseX; - S32 mLastMouseY; - LLButton* mPlayButton; - LLButton* mStopButton; - LLRect mPreviewRect; - LLRectf mPreviewImageRect; - LLAssetID mMotionID; - LLTransactionID mTransactionID; - BOOL mEnabled; - BOOL mInWorld; - LLAnimPauseRequest mPauseRequest; - - std::map mIDList; - - static S32 sUploadAmount; - - // - void* mItem; - // +class LLFloaterAnimPreview : public LLFloaterNameDesc { + public: + LLFloaterAnimPreview(LLSD const& filename); + virtual BOOL postBuild(void); }; #endif // LL_LLFLOATERANIMPREVIEW_H diff --git a/indra/newview/llfloaterblacklist.cpp b/indra/newview/llfloaterblacklist.cpp index 44e03ed16..0f5098e16 100644 --- a/indra/newview/llfloaterblacklist.cpp +++ b/indra/newview/llfloaterblacklist.cpp @@ -43,6 +43,25 @@ void LLFloaterBlacklist::show() sInstance->open(); } } +// static +void LLFloaterBlacklist::toggle() +{ + if (sInstance && sInstance->getVisible()) + { + delete sInstance; + } + else + { + show(); + } +} +// static +BOOL LLFloaterBlacklist::visible() +{ + if (sInstance && sInstance->getVisible()) + return TRUE; + return FALSE; +} BOOL LLFloaterBlacklist::postBuild() { childSetAction("add_btn", onClickAdd, this); diff --git a/indra/newview/llfloaterblacklist.h b/indra/newview/llfloaterblacklist.h index 6851e1a1e..9b5c4171f 100644 --- a/indra/newview/llfloaterblacklist.h +++ b/indra/newview/llfloaterblacklist.h @@ -11,6 +11,8 @@ public: LLFloaterBlacklist(); ~LLFloaterBlacklist(); static void show(); + static void toggle(); + static BOOL visible(); BOOL postBuild(); void refresh(); static LLFloaterBlacklist* getInstance() { return sInstance; }; diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp new file mode 100644 index 000000000..ced2258fc --- /dev/null +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -0,0 +1,1630 @@ +/** + * @file llfloaterbvhpreview.cpp + * @brief LLFloaterBvhPreview class implementation + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterbvhpreview.h" + +#include "llbvhloader.h" +#include "lldatapacker.h" +#include "lldir.h" +#include "llnotificationsutil.h" +#include "llvfile.h" +#include "llapr.h" +#include "llstring.h" + +#include "llagent.h" +#include "llanimationstates.h" +#include "llbbox.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "lldrawable.h" +#include "lldrawpoolavatar.h" +#include "llrender.h" +#include "llface.h" +#include "llfocusmgr.h" +#include "llkeyframemotion.h" +#include "lllineeditor.h" +#include "llfloaterperms.h" +#include "llsliderctrl.h" +#include "llspinctrl.h" +#include "lltextbox.h" +#include "lltoolmgr.h" +#include "llui.h" +#include "llviewercamera.h" +#include "llviewerobjectlist.h" +#include "llviewerwindow.h" +#include "llviewermenufile.h" // upload_new_resource() +#include "llvoavatarself.h" +#include "pipeline.h" +#include "lluictrlfactory.h" +#include "llviewercontrol.h" + +#include "hippogridmanager.h" + +// +#include "llinventorymodel.h" // gInventoryModel +// + +S32 LLFloaterBvhPreview::sUploadAmount = 10; + +const S32 PREVIEW_BORDER_WIDTH = 2; +const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH; +const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; +const S32 PREF_BUTTON_HEIGHT = 16; +const S32 PREVIEW_TEXTURE_HEIGHT = 300; + +const F32 PREVIEW_CAMERA_DISTANCE = 4.f; + +const F32 MIN_CAMERA_ZOOM = 0.5f; +const F32 MAX_CAMERA_ZOOM = 10.f; + +const F32 BASE_ANIM_TIME_OFFSET = 5.f; + +// +struct LLSaveInfo +{ + LLSaveInfo(const LLUUID& item_id, const LLUUID& object_id, const std::string& desc, + const LLTransactionID tid) + : mItemUUID(item_id), mObjectUUID(object_id), mDesc(desc), mTransactionID(tid) + { + } + + LLUUID mItemUUID; + LLUUID mObjectUUID; + std::string mDesc; + LLTransactionID mTransactionID; +}; +// + +std::string STATUS[] = +{ + "E_ST_OK", + "E_ST_EOF", + "E_ST_NO_CONSTRAINT", + "E_ST_NO_FILE", + "E_ST_NO_HIER", + "E_ST_NO_JOINT", + "E_ST_NO_NAME", + "E_ST_NO_OFFSET", + "E_ST_NO_CHANNELS", + "E_ST_NO_ROTATION", + "E_ST_NO_AXIS", + "E_ST_NO_MOTION", + "E_ST_NO_FRAMES", + "E_ST_NO_FRAME_TIME", + "E_ST_NO_POS", + "E_ST_NO_ROT", + "E_ST_NO_XLT_FILE", + "E_ST_NO_XLT_HEADER", + "E_ST_NO_XLT_NAME", + "E_ST_NO_XLT_IGNORE", + "E_ST_NO_XLT_RELATIVE", + "E_ST_NO_XLT_OUTNAME", + "E_ST_NO_XLT_MATRIX", + "E_ST_NO_XLT_MERGECHILD", + "E_ST_NO_XLT_MERGEPARENT", + "E_ST_NO_XLT_PRIORITY", + "E_ST_NO_XLT_LOOP", + "E_ST_NO_XLT_EASEIN", + "E_ST_NO_XLT_EASEOUT", + "E_ST_NO_XLT_HAND", + "E_ST_NO_XLT_EMOTE", +"E_ST_BAD_ROOT" +}; + + +//----------------------------------------------------------------------------- +// LLFloaterBvhPreview() +//----------------------------------------------------------------------------- +LLFloaterBvhPreview::LLFloaterBvhPreview(const std::string& filename, void* item) : + LLFloaterNameDesc(filename) +{ + // + mItem = item; + // + + mLastMouseX = 0; + mLastMouseY = 0; + + mIDList["Standing"] = ANIM_AGENT_STAND; + mIDList["Walking"] = ANIM_AGENT_FEMALE_WALK; + mIDList["Sitting"] = ANIM_AGENT_SIT_FEMALE; + mIDList["Flying"] = ANIM_AGENT_HOVER; + + mIDList["[None]"] = LLUUID::null; + mIDList["Aaaaah"] = ANIM_AGENT_EXPRESS_OPEN_MOUTH; + mIDList["Afraid"] = ANIM_AGENT_EXPRESS_AFRAID; + mIDList["Angry"] = ANIM_AGENT_EXPRESS_ANGER; + mIDList["Big Smile"] = ANIM_AGENT_EXPRESS_TOOTHSMILE; + mIDList["Bored"] = ANIM_AGENT_EXPRESS_BORED; + mIDList["Cry"] = ANIM_AGENT_EXPRESS_CRY; + mIDList["Disdain"] = ANIM_AGENT_EXPRESS_DISDAIN; + mIDList["Embarrassed"] = ANIM_AGENT_EXPRESS_EMBARRASSED; + mIDList["Frown"] = ANIM_AGENT_EXPRESS_FROWN; + mIDList["Kiss"] = ANIM_AGENT_EXPRESS_KISS; + mIDList["Laugh"] = ANIM_AGENT_EXPRESS_LAUGH; + mIDList["Plllppt"] = ANIM_AGENT_EXPRESS_TONGUE_OUT; + mIDList["Repulsed"] = ANIM_AGENT_EXPRESS_REPULSED; + mIDList["Sad"] = ANIM_AGENT_EXPRESS_SAD; + mIDList["Shrug"] = ANIM_AGENT_EXPRESS_SHRUG; + mIDList["Smile"] = ANIM_AGENT_EXPRESS_SMILE; + mIDList["Surprise"] = ANIM_AGENT_EXPRESS_SURPRISE; + mIDList["Wink"] = ANIM_AGENT_EXPRESS_WINK; + mIDList["Worry"] = ANIM_AGENT_EXPRESS_WORRY; +} + +//----------------------------------------------------------------------------- +// setAnimCallbacks() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::setAnimCallbacks() +{ + childSetCommitCallback("playback_slider", onSliderMove, this); + + childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); + childSetValue("preview_base_anim", "Standing"); + + childSetCommitCallback("priority", onCommitPriority, this); + childSetCommitCallback("loop_check", onCommitLoop, this); + childSetCommitCallback("loop_in_point", onCommitLoopIn, this); + childSetValidate("loop_in_point", validateLoopIn); + childSetCommitCallback("loop_out_point", onCommitLoopOut, this); + childSetValidate("loop_out_point", validateLoopOut); + + childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); + + childSetCommitCallback("emote_combo", onCommitEmote, this); + childSetValue("emote_combo", "[None]"); + + childSetCommitCallback("ease_in_time", onCommitEaseIn, this); + childSetValidate("ease_in_time", validateEaseIn); + childSetCommitCallback("ease_out_time", onCommitEaseOut, this); + childSetValidate("ease_out_time", validateEaseOut); +} + +//----------------------------------------------------------------------------- +// postBuild() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::postBuild() +{ + LLRect r; + LLKeyframeMotion* motionp = NULL; + LLBVHLoader* loaderp = NULL; + + if (!LLFloaterNameDesc::postBuild()) + { + return FALSE; + } + + mInWorld = gSavedSettings.getBOOL("PreviewAnimInWorld"); + + childSetCommitCallback("name_form", onCommitName, this); + + if (gSavedSettings.getBOOL("AscentPowerfulWizard")) + { + childSetMaxValue("priority", 7); + } + + childSetLabelArg("ok_btn", "[UPLOADFEE]", gHippoGridManager->getConnectedGrid()->getUploadFee()); + childSetAction("ok_btn", onBtnOK, this); + setDefaultBtn(); + + if (mInWorld) + { + r = getRect(); + translate(0, 230); + reshape(r.getWidth(), r.getHeight() - 230); + childSetValue("bad_animation_text", getString("in_world")); + childShow("bad_animation_text"); + } + else + { + childHide("bad_animation_text"); + } + + mPreviewRect.set(PREVIEW_HPAD, + PREVIEW_TEXTURE_HEIGHT, + getRect().getWidth() - PREVIEW_HPAD, + PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); + mPreviewImageRect.set(0.f, 1.f, 1.f, 0.f); + + S32 y = mPreviewRect.mTop + BTN_HEIGHT; + S32 btn_left = PREVIEW_HPAD; + + r.set( btn_left, y, btn_left + 32, y - BTN_HEIGHT ); + mPlayButton = getChild( "play_btn"); + if (!mPlayButton) + { + mPlayButton = new LLButton(std::string("play_btn"), LLRect(0,0,0,0)); + } + mPlayButton->setClickedCallback(boost::bind(&LLFloaterBvhPreview::onBtnPlay,this)); + + mPlayButton->setImages(std::string("button_anim_play.tga"), + std::string("button_anim_play_selected.tga")); + + mPlayButton->setImageDisabled(NULL); + mPlayButton->setImageDisabledSelected(NULL); + + mPlayButton->setScaleImage(TRUE); + + mStopButton = getChild( "stop_btn"); + if (!mStopButton) + { + mStopButton = new LLButton(std::string("stop_btn"), LLRect(0,0,0,0)); + } + mStopButton->setClickedCallback(boost::bind(&LLFloaterBvhPreview::onBtnStop, this)); + + mStopButton->setImages(std::string("button_anim_stop.tga"), + std::string("button_anim_stop_selected.tga")); + + mStopButton->setImageDisabled(NULL); + mStopButton->setImageDisabledSelected(NULL); + + mStopButton->setScaleImage(TRUE); + + r.set(r.mRight + PREVIEW_HPAD, y, getRect().getWidth() - PREVIEW_HPAD, y - BTN_HEIGHT); + //childSetCommitCallback("playback_slider", onSliderMove, this); + + //childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); + //childSetValue("preview_base_anim", "Standing"); + + //childSetCommitCallback("priority", onCommitPriority, this); + //childSetCommitCallback("loop_check", onCommitLoop, this); + //childSetCommitCallback("loop_in_point", onCommitLoopIn, this); + //childSetValidate("loop_in_point", validateLoopIn); + //childSetCommitCallback("loop_out_point", onCommitLoopOut, this); + //childSetValidate("loop_out_point", validateLoopOut); + + //childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); + + //childSetCommitCallback("emote_combo", onCommitEmote, this); + //childSetValue("emote_combo", "[None]"); + + //childSetCommitCallback("ease_in_time", onCommitEaseIn, this); + //childSetValidate("ease_in_time", validateEaseIn); + //childSetCommitCallback("ease_out_time", onCommitEaseOut, this); + //childSetValidate("ease_out_time", validateEaseOut); + + // moved declaration from below + BOOL success = false; + // + + std::string exten = gDirUtilp->getExtension(mFilename); + if (exten == "bvh") + { + // loading a bvh file + + // now load bvh file + S32 file_size; + + LLAPRFile infile(mFilenameAndPath, LL_APR_RB, &file_size); + + if (!infile.getFileHandle()) + { + llwarns << "Can't open BVH file:" << mFilename << llendl; + } + else + { + char* file_buffer; + + file_buffer = new char[file_size + 1]; + + if (file_size == infile.read(file_buffer, file_size)) + { + file_buffer[file_size] = '\0'; + llinfos << "Loading BVH file " << mFilename << llendl; + ELoadStatus load_status = E_ST_OK; + S32 line_number = 0; + loaderp = new LLBVHLoader(file_buffer, load_status, line_number); + std::string status = getString(STATUS[load_status]); + + if(load_status == E_ST_NO_XLT_FILE) + { + llwarns << "NOTE: No translation table found." << llendl; + } + else + { + llwarns << "ERROR: [line: " << line_number << "] " << status << llendl; + } + } + + infile.close() ; + delete[] file_buffer; + + // moved everything bvh from below + if(loaderp && loaderp->isInitialized() && loaderp->getDuration() <= MAX_ANIM_DURATION) + { + mTransactionID.generate(); + mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); + + mAnimPreview = new LLPreviewAnimation(256, 256); + + // motion will be returned, but it will be in a load-pending state, as this is a new motion + // this motion will not request an asset transfer until next update, so we have a chance to + // load the keyframe data locally + if (mInWorld) + { + motionp = (LLKeyframeMotion*)gAgentAvatarp->createMotion(mMotionID); + } + else + { + motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID); + } + + // create data buffer for keyframe initialization + S32 buffer_size = loaderp->getOutputSize(); + U8* buffer = new U8[buffer_size]; + + LLDataPackerBinaryBuffer dp(buffer, buffer_size); + + // pass animation data through memory buffer + loaderp->serialize(dp); + dp.reset(); + success = motionp && motionp->deserialize(dp); + } + else + { + success = false; + if ( loaderp ) + { + if (loaderp->getDuration() > MAX_ANIM_DURATION) + { + LLUIString out_str = getString("anim_too_long"); + out_str.setArg("[LENGTH]", llformat("%.1f", loaderp->getDuration())); + out_str.setArg("[MAX_LENGTH]", llformat("%.1f", MAX_ANIM_DURATION)); + getChild("bad_animation_text")->setValue(out_str.getString()); + } + else + { + LLUIString out_str = getString("failed_file_read"); + out_str.setArg("[STATUS]", getString(STATUS[loaderp->getStatus()])); + getChild("bad_animation_text")->setValue(out_str.getString()); + } + } + + //setEnabled(FALSE); + mMotionID.setNull(); + mAnimPreview = NULL; + } + // + } + } + // + else if(exten == "animatn") + { + S32 file_size; + LLAPRFile raw_animatn(mFilenameAndPath, LL_APR_RB, &file_size); + + if (!raw_animatn.getFileHandle()) + { + llwarns << "Can't open animatn file:" << mFilename << llendl; + } + else + { + char* file_buffer; + + file_buffer = new char[file_size + 1]; + + if (file_size == raw_animatn.read(file_buffer, file_size)) + { + file_buffer[file_size] = '\0'; + llinfos << "Loading animatn file " << mFilename << llendl; + mTransactionID.generate(); + mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); + mAnimPreview = new LLPreviewAnimation(256, 256); + motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID); + LLDataPackerBinaryBuffer dp((U8*)file_buffer, file_size); + dp.reset(); + success = motionp && motionp->deserialize(dp); + } + + raw_animatn.close(); + delete[] file_buffer; + } + } + // + + if (success) + { + setAnimCallbacks() ; + + if (!mInWorld) + { + const LLBBoxLocal &pelvis_bbox = motionp->getPelvisBBox(); + + LLVector3 temp = pelvis_bbox.getCenter(); + // only consider XY? + //temp.mV[VZ] = 0.f; + F32 pelvis_offset = temp.magVec(); + + temp = pelvis_bbox.getExtent(); + //temp.mV[VZ] = 0.f; + F32 pelvis_max_displacement = pelvis_offset + (temp.magVec() * 0.5f) + 1.f; + + F32 camera_zoom = LLViewerCamera::getInstance()->getDefaultFOV() / (2.f * atan(pelvis_max_displacement / PREVIEW_CAMERA_DISTANCE)); + + mAnimPreview->setZoom(camera_zoom); + } + + motionp->setName(childGetValue("name_form").asString()); + if (!mInWorld) + { + mAnimPreview->getDummyAvatar()->startMotion(mMotionID); + } + childSetMinValue("playback_slider", 0.0); + childSetMaxValue("playback_slider", 1.0); + + childSetValue("loop_check", LLSD(motionp->getLoop())); + childSetValue("loop_in_point", LLSD(motionp->getLoopIn() / motionp->getDuration() * 100.f)); + childSetValue("loop_out_point", LLSD(motionp->getLoopOut() / motionp->getDuration() * 100.f)); + childSetValue("priority", LLSD((F32)motionp->getPriority())); + childSetValue("hand_pose_combo", LLHandMotion::getHandPoseName(motionp->getHandPose())); + childSetValue("ease_in_time", LLSD(motionp->getEaseInDuration())); + childSetValue("ease_out_time", LLSD(motionp->getEaseOutDuration())); + setEnabled(TRUE); + std::string seconds_string; + seconds_string = llformat(" - %.2f seconds", motionp->getDuration()); + + setTitle(mFilename + std::string(seconds_string)); + } + else + { + mAnimPreview = NULL; + mMotionID.setNull(); + childSetValue("bad_animation_text", getString("failed_to_initialize")); + } + + + refresh(); + + delete loaderp; + + return TRUE; +} + +//----------------------------------------------------------------------------- +// LLFloaterBvhPreview() +//----------------------------------------------------------------------------- +LLFloaterBvhPreview::~LLFloaterBvhPreview() +{ + if (mInWorld) + { + LLVOAvatar* avatarp = gAgentAvatarp; + if (avatarp) + { + if (mMotionID.notNull()) + { + avatarp->stopMotion(mMotionID, TRUE); + avatarp->removeMotion(mMotionID); + } + avatarp->deactivateAllMotions(); + avatarp->startMotion(ANIM_AGENT_HEAD_ROT); + avatarp->startMotion(ANIM_AGENT_EYE); + avatarp->startMotion(ANIM_AGENT_BODY_NOISE); + avatarp->startMotion(ANIM_AGENT_BREATHE_ROT); + avatarp->startMotion(ANIM_AGENT_HAND_MOTION); + avatarp->startMotion(ANIM_AGENT_PELVIS_FIX); + avatarp->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET); + } + } + mAnimPreview = NULL; + + setEnabled(FALSE); +} + +//----------------------------------------------------------------------------- +// draw() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::draw() +{ + LLFloater::draw(); + LLRect r = getRect(); + + refresh(); + + if (mMotionID.notNull() && mAnimPreview && !mInWorld) + { + gGL.color3f(1.f, 1.f, 1.f); + + gGL.getTexUnit(0)->bind(mAnimPreview); + + gGL.begin( LLRender::QUADS ); + { + gGL.texCoord2f(0.f, 1.f); + gGL.vertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT); + } + gGL.end(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar(); + if (!avatarp->areAnimationsPaused()) + { + mAnimPreview->requestUpdate(); + } + } +} + +//----------------------------------------------------------------------------- +// resetMotion() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::resetMotion() +{ + LLVOAvatar* avatarp; + if (mInWorld) + { + avatarp = gAgentAvatarp; + } + else + { + avatarp = mAnimPreview->getDummyAvatar(); + } + if (!avatarp) + { + return; + } + + BOOL paused = avatarp->areAnimationsPaused(); + + // *TODO: Fix awful casting hack + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); + + // Set emotion + std::string emote = childGetValue("emote_combo").asString(); + motionp->setEmote(mIDList[emote]); + + LLUUID base_id = mIDList[childGetValue("preview_base_anim").asString()]; + avatarp->deactivateAllMotions(); + avatarp->startMotion(base_id, BASE_ANIM_TIME_OFFSET); + avatarp->startMotion(mMotionID, 0.0f); + childSetValue("playback_slider", 0.0f); + + // Set pose + std::string handpose = childGetValue("hand_pose_combo").asString(); + avatarp->startMotion( ANIM_AGENT_HAND_MOTION, 0.0f ); + motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + + if (paused) + { + mPauseRequest = avatarp->requestPause(); + } + else + { + mPauseRequest = NULL; + } +} + +//----------------------------------------------------------------------------- +// handleMouseDown() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (!mInWorld && mPreviewRect.pointInRect(x, y)) + { + bringToFront( x, y ); + gFocusMgr.setMouseCapture(this); + gViewerWindow->hideCursor(); + mLastMouseX = x; + mLastMouseY = y; + return TRUE; + } + + return LLFloater::handleMouseDown(x, y, mask); +} + +//----------------------------------------------------------------------------- +// handleMouseUp() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (!mInWorld) + { + gFocusMgr.setMouseCapture(FALSE); + gViewerWindow->showCursor(); + } + return LLFloater::handleMouseUp(x, y, mask); +} + +//----------------------------------------------------------------------------- +// handleHover() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::handleHover(S32 x, S32 y, MASK mask) +{ + if (mInWorld) + { + return TRUE; + } + + MASK local_mask = mask & ~MASK_ALT; + + if (mAnimPreview && hasMouseCapture()) + { + if (local_mask == MASK_PAN) + { + // pan here + mAnimPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f); + } + else if (local_mask == MASK_ORBIT) + { + F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; + F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f; + + mAnimPreview->rotate(yaw_radians, pitch_radians); + } + else + { + F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; + F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f; + + mAnimPreview->rotate(yaw_radians, 0.f); + mAnimPreview->zoom(zoom_amt); + } + + mAnimPreview->requestUpdate(); + + LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY); + } + + if (!mPreviewRect.pointInRect(x, y) || !mAnimPreview) + { + return LLFloater::handleHover(x, y, mask); + } + else if (local_mask == MASK_ORBIT) + { + gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA); + } + else if (local_mask == MASK_PAN) + { + gViewerWindow->setCursor(UI_CURSOR_TOOLPAN); + } + else + { + gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN); + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// handleScrollWheel() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + if (!mInWorld) + { + mAnimPreview->zoom((F32)clicks * -0.2f); + mAnimPreview->requestUpdate(); + } + return TRUE; +} + +//----------------------------------------------------------------------------- +// onMouseCaptureLost() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onMouseCaptureLost() +{ + if (!mInWorld) + { + gViewerWindow->showCursor(); + } +} + +//----------------------------------------------------------------------------- +// onBtnPlay() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onBtnPlay(void* user_data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)user_data; + if (!previewp->getEnabled()) + return; + + if (previewp->mMotionID.notNull()) + { + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + + if(!avatarp->isMotionActive(previewp->mMotionID)) + { + previewp->resetMotion(); + previewp->mPauseRequest = NULL; + } + else + { + if (avatarp->areAnimationsPaused()) + { + previewp->mPauseRequest = NULL; + } + else + { + previewp->mPauseRequest = avatarp->requestPause(); + } + } + } +} + +//----------------------------------------------------------------------------- +// onBtnStop() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onBtnStop(void* user_data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)user_data; + if (!previewp->getEnabled()) + return; + + if (previewp->mMotionID.notNull()) + { + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + previewp->resetMotion(); + previewp->mPauseRequest = avatarp->requestPause(); + } +} + +//----------------------------------------------------------------------------- +// onSliderMove() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onSliderMove(LLUICtrl* ctrl, void*user_data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)user_data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + F32 slider_value = (F32)previewp->childGetValue("playback_slider").asReal(); + LLUUID base_id = previewp->mIDList[previewp->childGetValue("preview_base_anim").asString()]; + LLMotion* motionp = avatarp->findMotion(previewp->mMotionID); + F32 duration = motionp->getDuration();// + motionp->getEaseOutDuration(); + F32 delta_time = duration * slider_value; + avatarp->deactivateAllMotions(); + avatarp->startMotion(base_id, delta_time + BASE_ANIM_TIME_OFFSET); + avatarp->startMotion(previewp->mMotionID, delta_time); + previewp->mPauseRequest = avatarp->requestPause(); + previewp->refresh(); +} + +//----------------------------------------------------------------------------- +// onCommitBaseAnim() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitBaseAnim(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + + BOOL paused = avatarp->areAnimationsPaused(); + + // stop all other possible base motions + avatarp->stopMotion(ANIM_AGENT_STAND, TRUE); + avatarp->stopMotion(ANIM_AGENT_WALK, TRUE); + avatarp->stopMotion(ANIM_AGENT_SIT, TRUE); + avatarp->stopMotion(ANIM_AGENT_HOVER, TRUE); + + previewp->resetMotion(); + + if (!paused) + { + previewp->mPauseRequest = NULL; + } +} + +//----------------------------------------------------------------------------- +// onCommitLoop() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitLoop(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (motionp) + { + motionp->setLoop(previewp->childGetValue("loop_check").asBoolean()); + motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() * 0.01f * motionp->getDuration()); + motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); + } +} + +//----------------------------------------------------------------------------- +// onCommitLoopIn() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitLoopIn(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (motionp) + { + motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() / 100.f); + previewp->resetMotion(); + previewp->childSetValue("loop_check", LLSD(TRUE)); + onCommitLoop(ctrl, data); + } +} + +//----------------------------------------------------------------------------- +// onCommitLoopOut() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitLoopOut(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (motionp) + { + motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); + previewp->resetMotion(); + previewp->childSetValue("loop_check", LLSD(TRUE)); + onCommitLoop(ctrl, data); + } +} + +//----------------------------------------------------------------------------- +// onCommitName() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitName(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (motionp) + { + motionp->setName(previewp->childGetValue("name_form").asString()); + } + + LLFloaterNameDesc::doCommit(ctrl, data); +} + +//----------------------------------------------------------------------------- +// onCommitHandPose() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitHandPose(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + previewp->resetMotion(); // sets hand pose +} + +//----------------------------------------------------------------------------- +// onCommitEmote() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitEmote(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + previewp->resetMotion(); // ssts emote +} + +//----------------------------------------------------------------------------- +// onCommitPriority() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitPriority(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + motionp->setPriority(llfloor((F32)previewp->childGetValue("priority").asReal())); +} + +//----------------------------------------------------------------------------- +// onCommitEaseIn() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitEaseIn(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + motionp->setEaseIn((F32)previewp->childGetValue("ease_in_time").asReal()); + previewp->resetMotion(); +} + +//----------------------------------------------------------------------------- +// onCommitEaseOut() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitEaseOut(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + motionp->setEaseOut((F32)previewp->childGetValue("ease_out_time").asReal()); + previewp->resetMotion(); +} + +//----------------------------------------------------------------------------- +// validateEaseIn() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::validateEaseIn(LLUICtrl* spin, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return FALSE; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return FALSE; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return FALSE; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (!motionp->getLoop()) + { + F32 new_ease_in = llclamp((F32)previewp->childGetValue("ease_in_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseOutDuration()); + previewp->childSetValue("ease_in_time", LLSD(new_ease_in)); + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// validateEaseOut() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::validateEaseOut(LLUICtrl* spin, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + + if (!previewp->getEnabled()) + return FALSE; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return FALSE; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return FALSE; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (!motionp->getLoop()) + { + F32 new_ease_out = llclamp((F32)previewp->childGetValue("ease_out_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseInDuration()); + previewp->childSetValue("ease_out_time", LLSD(new_ease_out)); + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// validateLoopIn() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::validateLoopIn(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return FALSE; + + F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); + F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); + + if (loop_in_value < 0.f) + { + loop_in_value = 0.f; + } + else if (loop_in_value > 100.f) + { + loop_in_value = 100.f; + } + else if (loop_in_value > loop_out_value) + { + loop_in_value = loop_out_value; + } + + previewp->childSetValue("loop_in_point", LLSD(loop_in_value)); + return TRUE; +} + +//----------------------------------------------------------------------------- +// validateLoopOut() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::validateLoopOut(LLUICtrl* spin, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return FALSE; + + F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); + F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); + + if (loop_out_value < 0.f) + { + loop_out_value = 0.f; + } + else if (loop_out_value > 100.f) + { + loop_out_value = 100.f; + } + else if (loop_out_value < loop_in_value) + { + loop_out_value = loop_in_value; + } + + previewp->childSetValue("loop_out_point", LLSD(loop_out_value)); + return TRUE; +} + + +//----------------------------------------------------------------------------- +// refresh() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::refresh() +{ + if (!mAnimPreview) + { + childShow("bad_animation_text"); + mPlayButton->setEnabled(FALSE); + mStopButton->setEnabled(FALSE); + childDisable("ok_btn"); + } + else + { + if (!mInWorld) + { + childHide("bad_animation_text"); + } + mPlayButton->setEnabled(TRUE); + LLVOAvatar* avatarp; + if (mInWorld) + { + avatarp = gAgentAvatarp; + } + else + { + avatarp = mAnimPreview->getDummyAvatar(); + } + if (avatarp->isMotionActive(mMotionID)) + { + mStopButton->setEnabled(TRUE); + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); + if (avatarp->areAnimationsPaused()) + { + + mPlayButton->setImages(std::string("button_anim_play.tga"), + std::string("button_anim_play_selected.tga")); + + } + else + { + if (motionp) + { + F32 fraction_complete = motionp->getLastUpdateTime() / motionp->getDuration(); + childSetValue("playback_slider", fraction_complete); + } + mPlayButton->setImages(std::string("button_anim_pause.tga"), + std::string("button_anim_pause_selected.tga")); + + } + } + else + { + mPauseRequest = avatarp->requestPause(); + mPlayButton->setImages(std::string("button_anim_play.tga"), + std::string("button_anim_play_selected.tga")); + + mStopButton->setEnabled(TRUE); // stop also resets, leave enabled. + } + childEnable("ok_btn"); + if (!mInWorld) + { + mAnimPreview->requestUpdate(); + } + } +} + +//----------------------------------------------------------------------------- +// onBtnOK() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onBtnOK(void* userdata) +{ + LLFloaterBvhPreview* floaterp = (LLFloaterBvhPreview*)userdata; + if (!floaterp->getEnabled()) return; + + if ((!floaterp->mInWorld && floaterp->mAnimPreview) || (floaterp->mInWorld && gAgentAvatarp)) + { + LLKeyframeMotion* motionp; + if (floaterp->mInWorld) + { + motionp = (LLKeyframeMotion*)gAgentAvatarp->findMotion(floaterp->mMotionID); + } + else + { + motionp = (LLKeyframeMotion*)floaterp->mAnimPreview->getDummyAvatar()->findMotion(floaterp->mMotionID); + } + + S32 file_size = motionp->getFileSize(); + U8* buffer = new U8[file_size]; + + LLDataPackerBinaryBuffer dp(buffer, file_size); + if (motionp->serialize(dp)) + { + LLVFile file(gVFS, motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND); + + S32 size = dp.getCurrentSize(); + file.setMaxSize(size); + if (file.write((U8*)buffer, size)) + { + std::string name = floaterp->childGetValue("name_form").asString(); + std::string desc = floaterp->childGetValue("description_form").asString(); + LLAssetStorage::LLStoreAssetCallback callback = NULL; + S32 expected_upload_cost = sUploadAmount; + void *userdata = NULL; + + // + if(floaterp->mItem) + { + // Update existing item instead of creating a new one + LLViewerInventoryItem* item = (LLViewerInventoryItem*)floaterp->mItem; + LLSaveInfo* info = new LLSaveInfo(item->getUUID(), LLUUID::null, desc, floaterp->mTransactionID); + gAssetStorage->storeAssetData(floaterp->mTransactionID, LLAssetType::AT_ANIMATION, NULL, info, FALSE); + + // I guess I will do this now because the floater is closing... + LLPointer new_item = new LLViewerInventoryItem(item); + new_item->setDescription(desc); + new_item->setTransactionID(floaterp->mTransactionID); + new_item->setAssetUUID(motionp->getID()); + new_item->updateServer(FALSE); + gInventory.updateItem(new_item); + gInventory.notifyObservers(); + } + else + // + { + upload_new_resource(floaterp->mTransactionID, // tid + LLAssetType::AT_ANIMATION, + name, + desc, + 0, + LLFolderType::FT_NONE, + LLInventoryType::IT_ANIMATION, + LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(), + name, + callback, expected_upload_cost, userdata); + } + } + else + { + llwarns << "Failure writing animation data." << llendl; + LLNotificationsUtil::add("WriteAnimationFail"); + } + } + + delete [] buffer; + // clear out cache for motion data + if (floaterp->mInWorld) + { + gAgentAvatarp->removeMotion(floaterp->mMotionID); + gAgentAvatarp->deactivateAllMotions(); + } + else + { + floaterp->mAnimPreview->getDummyAvatar()->removeMotion(floaterp->mMotionID); + } + LLKeyframeDataCache::removeKeyframeData(floaterp->mMotionID); + } + + floaterp->close(false); +} + +//----------------------------------------------------------------------------- +// LLPreviewAnimation +//----------------------------------------------------------------------------- +LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE) +{ + mNeedsUpdate = TRUE; + mCameraDistance = PREVIEW_CAMERA_DISTANCE; + mCameraYaw = 0.f; + mCameraPitch = 0.f; + mCameraZoom = 1.f; + + mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion()); + mDummyAvatar->createDrawable(&gPipeline); + mDummyAvatar->mIsDummy = TRUE; + mDummyAvatar->mSpecialRenderMode = 1; + mDummyAvatar->setPositionAgent(LLVector3::zero); + mDummyAvatar->slamPosition(); + mDummyAvatar->updateJointLODs(); + mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable); + mDummyAvatar->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET); + mDummyAvatar->hideSkirt(); + //gPipeline.markVisible(mDummyAvatar->mDrawable, *LLViewerCamera::getInstance()); + + // stop extraneous animations + mDummyAvatar->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE ); + mDummyAvatar->stopMotion( ANIM_AGENT_EYE, TRUE ); + mDummyAvatar->stopMotion( ANIM_AGENT_BODY_NOISE, TRUE ); + mDummyAvatar->stopMotion( ANIM_AGENT_BREATHE_ROT, TRUE ); +} + +//----------------------------------------------------------------------------- +// LLPreviewAnimation() +//----------------------------------------------------------------------------- +LLPreviewAnimation::~LLPreviewAnimation() +{ + mDummyAvatar->markDead(); +} + +//virtual +S8 LLPreviewAnimation::getType() const +{ + return LLViewerDynamicTexture::LL_PREVIEW_ANIMATION ; +} + +//----------------------------------------------------------------------------- +// update() +//----------------------------------------------------------------------------- +BOOL LLPreviewAnimation::render() +{ + mNeedsUpdate = FALSE; + LLVOAvatar* avatarp = mDummyAvatar; + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(0.0f, mFullWidth, 0.0f, mFullHeight, -1.0f, 1.0f); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); + + if (LLGLSLShader::sNoFixedFunction) + { + gUIProgram.bind(); + } + + LLGLSUIDefault def; + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.color4f(0.15f, 0.2f, 0.3f, 1.f); + + gl_rect_2d_simple( mFullWidth, mFullHeight ); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + + gGL.flush(); + + LLVector3 target_pos = avatarp->mRoot->getWorldPosition(); + + LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * + LLQuaternion(mCameraYaw, LLVector3::z_axis); + + LLQuaternion av_rot = avatarp->mRoot->getWorldRotation() * camera_rot; + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + mCameraOffset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos + (mCameraOffset * av_rot) ); // point of interest + + LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); + LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, FALSE); + + mCameraRelPos = LLViewerCamera::getInstance()->getOrigin() - avatarp->mHeadp->getWorldPosition(); + + //avatarp->setAnimationData("LookAtPoint", (void *)&mCameraRelPos); + + //SJB: Animation is updated in LLVOAvatar::updateCharacter + + if (avatarp->mDrawable.notNull()) + { + avatarp->updateLOD(); + + LLVertexBuffer::unbind(); + LLGLDepthTest gls_depth(GL_TRUE); + + LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool(); + avatarp->dirtyMesh(); + avatarPoolp->renderAvatars(avatarp); // renders only one avatar + } + + gGL.color4f(1,1,1,1); + return TRUE; +} + +//----------------------------------------------------------------------------- +// requestUpdate() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::requestUpdate() +{ + mNeedsUpdate = TRUE; +} + +//----------------------------------------------------------------------------- +// rotate() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::rotate(F32 yaw_radians, F32 pitch_radians) +{ + mCameraYaw = mCameraYaw + yaw_radians; + + mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); +} + +//----------------------------------------------------------------------------- +// zoom() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::zoom(F32 zoom_delta) +{ + setZoom(mCameraZoom + zoom_delta); +} + +//----------------------------------------------------------------------------- +// setZoom() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::setZoom(F32 zoom_amt) +{ + mCameraZoom = llclamp(zoom_amt, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM); +} + +//----------------------------------------------------------------------------- +// pan() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::pan(F32 right, F32 up) +{ + mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f); + mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f); +} + + + diff --git a/indra/newview/llfloaterbvhpreview.h b/indra/newview/llfloaterbvhpreview.h new file mode 100644 index 000000000..6a6adaa30 --- /dev/null +++ b/indra/newview/llfloaterbvhpreview.h @@ -0,0 +1,145 @@ +/** + * @file llfloaterbvhpreview.h + * @brief LLFloaterBvhPreview class definition + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERBVHPREVIEW_H +#define LL_LLFLOATERBVHPREVIEW_H + +#include "llfloaternamedesc.h" +#include "lldynamictexture.h" +#include "llcharacter.h" +#include "llquaternion.h" + +class LLVOAvatar; +class LLViewerJointMesh; + +class LLPreviewAnimation : public LLViewerDynamicTexture +{ +protected: + virtual ~LLPreviewAnimation(); + +public: + LLPreviewAnimation(S32 width, S32 height); + + /*virtual*/ S8 getType() const ; + + BOOL render(); + void requestUpdate(); + void rotate(F32 yaw_radians, F32 pitch_radians); + void zoom(F32 zoom_delta); + void setZoom(F32 zoom_amt); + void pan(F32 right, F32 up); + virtual BOOL needsUpdate() { return mNeedsUpdate; } + + LLVOAvatar* getDummyAvatar() { return mDummyAvatar; } + +protected: + BOOL mNeedsUpdate; + F32 mCameraDistance; + F32 mCameraYaw; + F32 mCameraPitch; + F32 mCameraZoom; + LLVector3 mCameraOffset; + LLVector3 mCameraRelPos; + LLPointer mDummyAvatar; +}; + +class LLFloaterBvhPreview : public LLFloaterNameDesc +{ +public: + // + LLFloaterBvhPreview(const std::string& filename, void* item = NULL); + // + virtual ~LLFloaterBvhPreview(); + + BOOL postBuild(); + + BOOL handleMouseDown(S32 x, S32 y, MASK mask); + BOOL handleMouseUp(S32 x, S32 y, MASK mask); + BOOL handleHover(S32 x, S32 y, MASK mask); + BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + void onMouseCaptureLost(); + + void refresh(); + + static void onBtnPlay(void*); + static void onBtnStop(void*); + static void setUploadAmount(S32 amount) { sUploadAmount = amount; } + static void onSliderMove(LLUICtrl*, void*); + static void onCommitBaseAnim(LLUICtrl*, void*); + static void onCommitLoop(LLUICtrl*, void*); + static void onCommitLoopIn(LLUICtrl*, void*); + static void onCommitLoopOut(LLUICtrl*, void*); + static BOOL validateLoopIn(LLUICtrl*, void*); + static BOOL validateLoopOut(LLUICtrl*, void*); + static void onCommitName(LLUICtrl*, void*); + static void onCommitHandPose(LLUICtrl*, void*); + static void onCommitEmote(LLUICtrl*, void*); + static void onCommitPriority(LLUICtrl*, void*); + static void onCommitEaseIn(LLUICtrl*, void*); + static void onCommitEaseOut(LLUICtrl*, void*); + static BOOL validateEaseIn(LLUICtrl*, void*); + static BOOL validateEaseOut(LLUICtrl*, void*); + static void onBtnOK(void*); + static void onSaveComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, + S32 status, LLExtStat ext_status); +private: + void setAnimCallbacks() ; + +protected: + void draw(); + void resetMotion(); + + LLPointer< LLPreviewAnimation> mAnimPreview; + S32 mLastMouseX; + S32 mLastMouseY; + LLButton* mPlayButton; + LLButton* mStopButton; + LLRect mPreviewRect; + LLRectf mPreviewImageRect; + LLAssetID mMotionID; + LLTransactionID mTransactionID; + BOOL mEnabled; + BOOL mInWorld; + LLAnimPauseRequest mPauseRequest; + + std::map mIDList; + + static S32 sUploadAmount; + + // + void* mItem; + // +}; + +#endif // LL_LLFLOATERBVHPREVIEW_H diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index 0c3046680..c61d05f74 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -189,6 +189,7 @@ LLFloaterCustomize::~LLFloaterCustomize() BOOL LLFloaterCustomize::postBuild() { getChild("Make Outfit")->setCommitCallback(boost::bind(&LLFloaterCustomize::onBtnMakeOutfit, this)); + getChild("Save Outfit")->setCommitCallback(boost::bind(&LLAppearanceMgr::updateBaseOutfit, LLAppearanceMgr::getInstance())); getChild("Ok")->setCommitCallback(boost::bind(&LLFloaterCustomize::onBtnOk, this)); getChild("Cancel")->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); diff --git a/indra/newview/llfloaterexploreanimations.cpp b/indra/newview/llfloaterexploreanimations.cpp index 9a973c8c8..6267b31c5 100644 --- a/indra/newview/llfloaterexploreanimations.cpp +++ b/indra/newview/llfloaterexploreanimations.cpp @@ -4,7 +4,7 @@ #include "llfloaterexploreanimations.h" #include "lluictrlfactory.h" #include "llscrolllistctrl.h" -#include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llvoavatar.h" #include "lllocalinventory.h" #include "llviewercamera.h" diff --git a/indra/newview/llfloaterexploreanimations.h b/indra/newview/llfloaterexploreanimations.h index 27c4af3c1..383b16e6d 100644 --- a/indra/newview/llfloaterexploreanimations.h +++ b/indra/newview/llfloaterexploreanimations.h @@ -3,7 +3,7 @@ #define LL_LLFLOATEREXPLOREANIMATIONS_H #include "llfloater.h" -#include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llviewerwindow.h" // gViewerWindow class LLAnimHistoryItem diff --git a/indra/newview/llfloaterexploresounds.cpp b/indra/newview/llfloaterexploresounds.cpp index 4ce0cf111..0e3934e2f 100644 --- a/indra/newview/llfloaterexploresounds.cpp +++ b/indra/newview/llfloaterexploresounds.cpp @@ -68,6 +68,13 @@ void LLFloaterExploreSounds::toggle() else LLFloaterExploreSounds::sInstance = new LLFloaterExploreSounds(); } +BOOL LLFloaterExploreSounds::visible() +{ + if (LLFloaterExploreSounds::sInstance) + return TRUE; + return FALSE; +} + void LLFloaterExploreSounds::close(bool app_quitting) { LLFloater::close(app_quitting); diff --git a/indra/newview/llfloaterexploresounds.h b/indra/newview/llfloaterexploresounds.h index 4bdcae820..7bb7ae1c5 100644 --- a/indra/newview/llfloaterexploresounds.h +++ b/indra/newview/llfloaterexploresounds.h @@ -36,6 +36,7 @@ public: static LLFloaterExploreSounds* sInstance; static void toggle(); + static BOOL visible(); }; #endif diff --git a/indra/newview/llfloaterstats.cpp b/indra/newview/llfloaterstats.cpp index a60619882..342e4fa45 100644 --- a/indra/newview/llfloaterstats.cpp +++ b/indra/newview/llfloaterstats.cpp @@ -238,25 +238,53 @@ void LLFloaterStats::buildStats() // Network statistics LLStatView *net_statviewp = stat_viewp->addStatView("network stat view", "Network", "OpenDebugStatNet", rect); - stat_barp = net_statviewp->addStat("Packets In", &(LLViewerStats::getInstance()->mPacketsInStat), "DebugStatModePacketsIn"); + stat_barp = net_statviewp->addStat("UDP Packets In", &(LLViewerStats::getInstance()->mPacketsInStat), "DebugStatModePacketsIn"); stat_barp->setUnitLabel("/sec"); - stat_barp = net_statviewp->addStat("Packets Out", &(LLViewerStats::getInstance()->mPacketsOutStat), "DebugStatModePacketsOut"); + stat_barp = net_statviewp->addStat("UDP Packets Out", &(LLViewerStats::getInstance()->mPacketsOutStat), "DebugStatModePacketsOut"); stat_barp->setUnitLabel("/sec"); - stat_barp = net_statviewp->addStat("Objects", &(LLViewerStats::getInstance()->mObjectKBitStat), "DebugStatModeObjects"); + stat_barp = net_statviewp->addStat("HTTP Textures", &(LLViewerStats::getInstance()->mHTTPTextureKBitStat), "DebugStatModeHTTPTexture"); stat_barp->setUnitLabel(" kbps"); + stat_barp->mMinBar = 0.f; + stat_barp->mMaxBar = gSavedSettings.getF32("HTTPThrottleBandwidth"); + stat_barp->mMaxBar *= llclamp(2.0 - (stat_barp->mMaxBar - 400.f) / 3600.f, 1.0, 2.0); // Allow for overshoot (allow more for low bandwidth values). + stat_barp->mTickSpacing = 1.f; + while (stat_barp->mTickSpacing < stat_barp->mMaxBar / 8) + stat_barp->mTickSpacing *= 2.f; + stat_barp->mLabelSpacing = 2 * stat_barp->mTickSpacing; + stat_barp->mPerSec = FALSE; + stat_barp->mDisplayMean = FALSE; - stat_barp = net_statviewp->addStat("Texture", &(LLViewerStats::getInstance()->mTextureKBitStat), "DebugStatModeTexture"); + stat_barp = net_statviewp->addStat("UDP Textures", &(LLViewerStats::getInstance()->mUDPTextureKBitStat), "DebugStatModeUDPTexture"); stat_barp->setUnitLabel(" kbps"); + stat_barp->mMinBar = 0.f; + stat_barp->mMaxBar = 1024.f; + stat_barp->mTickSpacing = 128.f; + stat_barp->mLabelSpacing = 256.f; - stat_barp = net_statviewp->addStat("Asset", &(LLViewerStats::getInstance()->mAssetKBitStat), "DebugStatModeAsset"); + stat_barp = net_statviewp->addStat("Objects (UDP)", &(LLViewerStats::getInstance()->mObjectKBitStat), "DebugStatModeObjects"); stat_barp->setUnitLabel(" kbps"); + stat_barp->mMinBar = 0.f; + stat_barp->mMaxBar = 1024.f; + stat_barp->mTickSpacing = 128.f; + stat_barp->mLabelSpacing = 256.f; - stat_barp = net_statviewp->addStat("Layers", &(LLViewerStats::getInstance()->mLayersKBitStat), "DebugStatModeLayers"); + stat_barp = net_statviewp->addStat("Assets (UDP)", &(LLViewerStats::getInstance()->mAssetKBitStat), "DebugStatModeAsset"); stat_barp->setUnitLabel(" kbps"); + stat_barp->mMinBar = 0.f; + stat_barp->mMaxBar = 1024.f; + stat_barp->mTickSpacing = 128.f; + stat_barp->mLabelSpacing = 256.f; - stat_barp = net_statviewp->addStat("Actual In", &(LLViewerStats::getInstance()->mActualInKBitStat), + stat_barp = net_statviewp->addStat("Layers (UDP)", &(LLViewerStats::getInstance()->mLayersKBitStat), "DebugStatModeLayers"); + stat_barp->setUnitLabel(" kbps"); + stat_barp->mMinBar = 0.f; + stat_barp->mMaxBar = 1024.f; + stat_barp->mTickSpacing = 128.f; + stat_barp->mLabelSpacing = 256.f; + + stat_barp = net_statviewp->addStat("Actual In (UDP)", &(LLViewerStats::getInstance()->mActualInKBitStat), "DebugStatModeActualIn", TRUE, FALSE); stat_barp->setUnitLabel(" kbps"); stat_barp->mMinBar = 0.f; @@ -264,7 +292,7 @@ void LLFloaterStats::buildStats() stat_barp->mTickSpacing = 128.f; stat_barp->mLabelSpacing = 256.f; - stat_barp = net_statviewp->addStat("Actual Out", &(LLViewerStats::getInstance()->mActualOutKBitStat), + stat_barp = net_statviewp->addStat("Actual Out (UDP)", &(LLViewerStats::getInstance()->mActualOutKBitStat), "DebugStatModeActualOut", TRUE, FALSE); stat_barp->setUnitLabel(" kbps"); stat_barp->mMinBar = 0.f; diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index ed0c6c54e..fe07063bb 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -31,11 +31,14 @@ */ #include "llviewerprecompiledheaders.h" + #include "llagentwearables.h" +#include "llappearancemgr.h" +#include "llfoldervieweventlistener.h" #include "llimview.h" -#include "llinventoryfunctions.h" #include "llinventorybridge.h" #include "llinventoryclipboard.h" +#include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" #include "llinventorypanel.h" #include "llmakeoutfitdialog.h" @@ -43,7 +46,6 @@ #include "llpanelmaininventory.h" #include "llpanelobjectinventory.h" #include "llpreview.h" // For LLMultiPreview -#include "llfoldervieweventlistener.h" #include "lltrans.h" #include "llvoavatarself.h" @@ -317,7 +319,7 @@ void do_create(LLInventoryModel *model, LLInventoryPanel *ptr, std::string type, LLInventoryType::IT_GESTURE, PERM_ALL); } - else if ("outfit" == type) + else if ("outfit" == type || ("update outfit" == type && !LLAppearanceMgr::getInstance()->updateBaseOutfit())) // If updateBaseOutfit fails, prompt to make a new outfit { new LLMakeOutfitDialog(false); return; diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 6b88ab5c5..0f1d93c5c 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -37,6 +37,7 @@ #include "llviewermessage.h" #include "llviewerregion.h" #include "llviewerwindow.h" +#include "hippogridmanager.h" class AIHTTPTimeoutPolicy; extern AIHTTPTimeoutPolicy inventoryModelFetchDescendentsResponder_timeout; @@ -115,7 +116,6 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) { // it's a folder, do a bulk fetch LL_DEBUGS("InventoryFetch") << "Start fetching category: " << id << ", recursive: " << recursive << LL_ENDL; - mBackgroundFetchActive = TRUE; mFolderFetchActive = true; if (id.isNull()) { @@ -124,12 +124,14 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) mRecursiveInventoryFetchStarted |= recursive; mFetchQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursive)); gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + mBackgroundFetchActive = TRUE; } if (!mRecursiveLibraryFetchStarted) { mRecursiveLibraryFetchStarted |= recursive; mFetchQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursive)); gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + mBackgroundFetchActive = TRUE; } } else @@ -139,6 +141,7 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) { mFetchQueue.push_front(FetchQueueInfo(id, recursive)); gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + mBackgroundFetchActive = TRUE; } if (id == gInventory.getLibraryRootFolderID()) { @@ -178,6 +181,11 @@ void LLInventoryModelBackgroundFetch::setAllFoldersFetched() mAllFoldersFetched = TRUE; } mFolderFetchActive = false; + if (mBackgroundFetchActive) + { + gIdleCallbacks.deleteFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + mBackgroundFetchActive = FALSE; + } } void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *) @@ -190,14 +198,27 @@ void LLInventoryModelBackgroundFetch::backgroundFetch() LLViewerRegion* region = gAgent.getRegion(); if (mBackgroundFetchActive && region && region->capabilitiesReceived()) { - // If we'll be using the capability, we'll be sending batches and the background thing isn't as important. - std::string url = region->getCapability("FetchInventory2"); - if (gSavedSettings.getBOOL("UseHTTPInventory") && !url.empty()) + if (gSavedSettings.getBOOL("UseHTTPInventory")) { - bulkFetch(); - return; - } - + // If we'll be using the capability, we'll be sending batches and the background thing isn't as important. + std::string url = region->getCapability("FetchInventory2"); + if (!url.empty()) + { + if (!mPerServicePtr) + { + // One time initialization needed for bulkFetch(). + std::string servicename = AIPerService::extract_canonical_servicename(url); + if (!servicename.empty()) + { + llinfos << "Initialized service name for bulk inventory fetching with \"" << servicename << "\"." << llendl; + mPerServicePtr = AIPerService::instance(servicename); + } + } + bulkFetch(); + return; + } + } + #if 1 //-------------------------------------------------------------------------------- // DEPRECATED OLD CODE @@ -207,10 +228,7 @@ void LLInventoryModelBackgroundFetch::backgroundFetch() if (mFetchQueue.empty()) { llinfos << "Inventory fetch completed" << llendl; - setAllFoldersFetched(); - mBackgroundFetchActive = false; - mFolderFetchActive = false; return; } @@ -395,7 +413,6 @@ class LLInventoryModelFetchDescendentsResponder : public LLHTTPClient::Responder mRequestSD(request_sd), mRecursiveCatUUIDs(recursive_cats) {}; - //LLInventoryModelFetchDescendentsResponder() {}; /*virtual*/ void result(const LLSD& content); /*virtual*/ void error(U32 status, const std::string& reason); /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return inventoryModelFetchDescendentsResponder_timeout; } @@ -575,28 +592,22 @@ BOOL LLInventoryModelFetchDescendentsResponder::getIsRecursive(const LLUUID& cat } // Bundle up a bunch of requests to send all at once. -// static void LLInventoryModelBackgroundFetch::bulkFetch() { //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. //If there are items in mFetchQueue, we want to check the time since the last bulkFetch was //sent. If it exceeds our retry time, go ahead and fire off another batch. LLViewerRegion* region = gAgent.getRegion(); - if (!region) return; + if (gDisconnected || !region) return; - S16 max_concurrent_fetches=8; - F32 new_min_time = 0.5f; //HACK! Clean this up when old code goes away entirely. - if (mMinTimeBetweenFetches < new_min_time) + if (!AIPerService::wantsMoreHTTPRequestsFor(mPerServicePtr)) { - mMinTimeBetweenFetches=new_min_time; //HACK! See above. + return; // Wait. } - - if (gDisconnected || - (mFetchCount > max_concurrent_fetches) || - (mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches)) - { - return; // just bail if we are disconnected - } + // If AIPerService::wantsMoreHTTPRequestsFor returns true, then it approved ONE request. + // The code below might fire off zero, one or even more than one requests however! + // This object keeps track of that. + AIPerService::Approvement approvement(mPerServicePtr); U32 item_count=0; U32 folder_count=0; @@ -690,14 +701,16 @@ void LLInventoryModelBackgroundFetch::bulkFetch() if (folder_request_body["folders"].size()) { LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body, recursive_cats); - LLHTTPClient::post(url, folder_request_body, fetcher); + LLHTTPClient::post_nb(url, folder_request_body, fetcher); + approvement.honored(); } if (folder_request_body_lib["folders"].size()) { std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents2"); LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body_lib, recursive_cats); - LLHTTPClient::post(url_lib, folder_request_body_lib, fetcher); + LLHTTPClient::post_nb(url_lib, folder_request_body_lib, fetcher); + approvement.honored(); } } if (item_count) @@ -708,26 +721,16 @@ void LLInventoryModelBackgroundFetch::bulkFetch() { mFetchCount++; url = region->getCapability("FetchInventory2"); + llassert(!url.empty()); if (!url.empty()) { LLSD body; body["agent_id"] = gAgent.getID(); body["items"] = item_request_body; - LLHTTPClient::post(url, body, new LLInventoryModelFetchItemResponder(body)); + LLHTTPClient::post_nb(url, body, new LLInventoryModelFetchItemResponder(body)); + approvement.honored(); } - //else - //{ - // LLMessageSystem* msg = gMessageSystem; - // msg->newMessage("FetchInventory"); - // msg->nextBlock("AgentData"); - // msg->addUUID("AgentID", gAgent.getID()); - // msg->addUUID("SessionID", gAgent.getSessionID()); - // msg->nextBlock("InventoryData"); - // msg->addUUID("OwnerID", mPermissions.getOwner()); - // msg->addUUID("ItemID", mUUID); - // gAgent.sendReliableMessage(); - //} } if (item_request_body_lib.size()) @@ -735,21 +738,23 @@ void LLInventoryModelBackgroundFetch::bulkFetch() mFetchCount++; url = region->getCapability("FetchLib2"); + llassert(!url.empty()); if (!url.empty()) { LLSD body; body["agent_id"] = gAgent.getID(); body["items"] = item_request_body_lib; - LLHTTPClient::post(url, body, new LLInventoryModelFetchItemResponder(body)); + LLHTTPClient::post_nb(url, body, new LLInventoryModelFetchItemResponder(body)); + approvement.honored(); } } } mFetchTimer.reset(); } - else if (isBulkFetchProcessingComplete()) { + llinfos << "Inventory fetch completed" << llendl; setAllFoldersFetched(); } } diff --git a/indra/newview/llinventorymodelbackgroundfetch.h b/indra/newview/llinventorymodelbackgroundfetch.h index bc40cc5cb..4056bdfbd 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.h +++ b/indra/newview/llinventorymodelbackgroundfetch.h @@ -29,6 +29,7 @@ #include "llsingleton.h" #include "lluuid.h" +#include "aicurlperservice.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInventoryModelBackgroundFetch @@ -75,12 +76,14 @@ private: BOOL mRecursiveLibraryFetchStarted; BOOL mAllFoldersFetched; - BOOL mBackgroundFetchActive; + BOOL mBackgroundFetchActive; // TRUE if LLInventoryModelBackgroundFetch::backgroundFetchCB is being called from idle(). bool mFolderFetchActive; S16 mFetchCount; BOOL mTimelyFetchPending; S32 mNumFetchRetries; + AIPerServicePtr mPerServicePtr; // Pointer to the AIPerService corresponding to the FetchInventory2 capability. + LLFrameTimer mFetchTimer; F32 mMinTimeBetweenFetches; F32 mMaxTimeBetweenFetches; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 88f6c8e38..4bf236f64 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1329,9 +1329,10 @@ void LLMeshUploadThread::preStart() } AIMeshUpload::AIMeshUpload(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, std::string const& upload_url, bool do_upload, - LLHandle const& fee_observer, LLHandle const& upload_observer) : mWholeModelUploadURL(upload_url) + LLHandle const& fee_observer, LLHandle const& upload_observer) : + mMeshUpload(new AIStateMachineThread), mWholeModelUploadURL(upload_url) { - mMeshUpload->init(data, scale, upload_textures, upload_skin, upload_joints, do_upload, fee_observer, upload_observer); + mMeshUpload->thread_impl().init(data, scale, upload_textures, upload_skin, upload_joints, do_upload, fee_observer, upload_observer); } char const* AIMeshUpload::state_str_impl(state_type run_state) const @@ -1347,7 +1348,7 @@ char const* AIMeshUpload::state_str_impl(state_type run_state) const void AIMeshUpload::initialize_impl() { - mMeshUpload->preStart(); + mMeshUpload->thread_impl().preStart(); set_state(AIMeshUpload_start); } @@ -1356,11 +1357,11 @@ void AIMeshUpload::multiplex_impl(state_type run_state) switch (run_state) { case AIMeshUpload_start: - mMeshUpload.run(this, AIMeshUpload_threadFinished); + mMeshUpload->run(this, AIMeshUpload_threadFinished); idle(); // Wait till the thread finished. break; case AIMeshUpload_threadFinished: - mMeshUpload->postRequest(mWholeModelUploadURL, this); + mMeshUpload->thread_impl().postRequest(mWholeModelUploadURL, this); idle(); // Wait till the responder finished. break; case AIMeshUpload_responderFinished: @@ -2508,7 +2509,7 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, } S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) -{ +{ return mThread->getActualMeshLOD(mesh_params, lod); } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index d26316a85..5f397c206 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -32,6 +32,7 @@ #include "lluuid.h" #include "llviewertexture.h" #include "llvolume.h" +#include "sguuidhash.h" #define LLCONVEXDECOMPINTER_STATIC 1 @@ -433,7 +434,7 @@ enum AIMeshUpload_state_type { class AIMeshUpload : public AIStateMachine { private: - AIStateMachineThread mMeshUpload; + LLPointer > mMeshUpload; std::string mWholeModelUploadURL; public: @@ -505,8 +506,8 @@ public: typedef std::map > mesh_load_map; mesh_load_map mLoadingMeshes[4]; - - typedef std::map skin_map; + + typedef boost::unordered_map skin_map; skin_map mSkinMap; typedef std::map decomposition_map; diff --git a/indra/newview/llpaneldisplay.cpp b/indra/newview/llpaneldisplay.cpp index d354dc516..a6a181014 100644 --- a/indra/newview/llpaneldisplay.cpp +++ b/indra/newview/llpaneldisplay.cpp @@ -885,7 +885,7 @@ void LLPanelDisplay::apply() void LLPanelDisplay::onChangeQuality(LLUICtrl* caller) { - LLSliderCtrl* sldr = static_cast(caller); + LLSlider* sldr = dynamic_cast(caller); if(sldr == NULL) { diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 2fc014216..7f43b4ce3 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -491,7 +491,7 @@ void LLPanelFace::sendTextureInfo() return (object->mDrawable) ? object->mDrawable->getFace(te): NULL; } } get_last_face_func; - LLFace* last_face; + LLFace* last_face(NULL); LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_last_face_func, last_face); LLPanelFaceSetAlignedTEFunctor setfunc(this, last_face); @@ -654,7 +654,7 @@ void LLPanelFace::getState() return (object->mDrawable) ? object->mDrawable->getFace(te): NULL; } } get_te_face_func; - LLFace* last_face; + LLFace* last_face(NULL); LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_te_face_func, last_face); LLPanelFaceGetIsAlignedTEFunctor get_is_aligend_func(last_face); // this will determine if the texture param controls are tentative: diff --git a/indra/newview/llpanelnetwork.cpp b/indra/newview/llpanelnetwork.cpp index 86e6bced5..95b91731b 100644 --- a/indra/newview/llpanelnetwork.cpp +++ b/indra/newview/llpanelnetwork.cpp @@ -44,6 +44,7 @@ #include "llnotificationsutil.h" #include "llviewercontrol.h" #include "llviewerwindow.h" +#include "hippogridmanager.h" #include "llproxy.h" @@ -74,6 +75,14 @@ BOOL LLPanelNetwork::postBuild() childSetValue("connection_port_enabled", gSavedSettings.getBOOL("ConnectionPortEnabled")); childSetValue("connection_port", (F32)gSavedSettings.getU32("ConnectionPort")); + // If in Avination, hide the texture bandwidth slider, Avination throttles server-side + if (gHippoGridManager->getConnectedGrid()->isAvination()) + { + childSetVisible("text_box4", FALSE); + childSetVisible("tex_bandwidth", FALSE); + childSetVisible("text_box3", FALSE); + } + // Socks 5 proxy settings, commit callbacks childSetCommitCallback("socks5_proxy_enabled", onCommitSocks5ProxyEnabled, this); childSetCommitCallback("socks5_auth", onSocksAuthChanged, this); diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index d01185294..1d4c39294 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -772,7 +772,7 @@ void LLPanelPermissions::refresh() { childSetEnabled("checkbox allow export", false); if (!gHippoGridManager->getCurrentGrid()->isSecondLife()) - childSetVisible("checkbox allow export", false); + childSetVisible("checkbox allow export", true); } if (has_change_sale_ability && (owner_mask_on & PERM_TRANSFER)) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 3ecc2aa4d..455cf3820 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -212,6 +212,7 @@ #include "llinventorybridge.h" #include "llappearancemgr.h" #include "jcfloaterareasearch.h" +#include "generichandlers.h" // #include "llpanellogin.h" @@ -843,6 +844,8 @@ bool idle_startup() // *NOTE: This is where gMuteList used to get allocated before becoming LLMuteList::getInstance(). + gGenericHandlers = new GenericHandlers(); + // Initialize UI if (!gNoRender) { @@ -999,11 +1002,14 @@ bool idle_startup() if(!gHippoGridManager->getConnectedGrid()->isSecondLife()) { LLTrans::setDefaultArg("[CURRENCY]",gHippoGridManager->getConnectedGrid()->getCurrencySymbol()); //replace [CURRENCY] with OS$, not L$ for instance. + LLTrans::setDefaultArg("[CURRENCY_TEXT]",gHippoGridManager->getConnectedGrid()->getCurrencyText()); //replace [CURRENCYTEXT] with OS DOllars, not Linden Dollars for instance. LLTrans::setDefaultArg("[SECOND_LIFE]", gHippoGridManager->getConnectedGrid()->getGridName()); LLTrans::setDefaultArg("[SECOND_LIFE_GRID]", gHippoGridManager->getConnectedGrid()->getGridName() + " Grid"); LLTrans::setDefaultArg("[GRID_OWNER]", gHippoGridManager->getConnectedGrid()->getGridOwner()); LLScriptEdCore::parseFunctions("lsl_functions_os.xml"); //Singu Note: This appends to the base functions parsed from lsl_functions_sl.xml } + // Avination doesn't want the viewer to do bandwidth throttling (it is done serverside, taking UDP into account too). + AIPerService::setNoHTTPBandwidthThrottling(gHippoGridManager->getConnectedGrid()->isAvination()); // create necessary directories // *FIX: these mkdir's should error check @@ -4375,6 +4381,8 @@ bool process_login_success_response(std::string& password) if (!tmp.empty()) gHippoGridManager->getConnectedGrid()->setSearchUrl(tmp); tmp = response["currency"].asString(); if (!tmp.empty()) gHippoGridManager->getConnectedGrid()->setCurrencySymbol(tmp); + tmp = response["currency_text"].asString(); + if (!tmp.empty()) gHippoGridManager->getConnectedGrid()->setCurrencyText(tmp); tmp = response["real_currency"].asString(); if (!tmp.empty()) gHippoGridManager->getConnectedGrid()->setRealCurrencySymbol(tmp); tmp = response["directory_fee"].asString(); @@ -4385,6 +4393,8 @@ bool process_login_success_response(std::string& password) if (!tmp.empty()) gHippoGridManager->getConnectedGrid()->setMaxAgentGroups(atoi(tmp.c_str())); tmp = response["VoiceConnector"].asString(); if (!tmp.empty()) gHippoGridManager->getConnectedGrid()->setVoiceConnector(tmp); + tmp = response["upc_supported"].asString(); + if (!tmp.empty()) gHippoGridManager->getConnectedGrid()->setUPCSupported(true); gHippoGridManager->saveFile(); gHippoLimits->setLimits(); diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 631527d24..0563a150c 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -68,6 +68,7 @@ #include "llworld.h" #include "llstatgraph.h" #include "llviewercontrol.h" +#include "llviewergenericmessage.h" #include "llviewermenu.h" // for gMenuBarView #include "llviewerparcelmgr.h" #include "llviewerthrottle.h" @@ -137,9 +138,27 @@ std::vector LLStatusBar::sDays; std::vector LLStatusBar::sMonths; const U32 LLStatusBar::MAX_DATE_STRING_LENGTH = 2000; +class LLDispatchUPCBalance : public LLDispatchHandler +{ +public: + virtual bool operator()( + const LLDispatcher* dispatcher, + const std::string& key, + const LLUUID& invoice, + const sparam_t& strings) + { + S32 upc = atoi(strings[0].c_str()); + gStatusBar->setUPC(upc); + return true; + } +}; + +static LLDispatchUPCBalance sDispatchUPCBalance; + LLStatusBar::LLStatusBar(const std::string& name, const LLRect& rect) : LLPanel(name, LLRect(), FALSE), // not mouse opaque mBalance(0), +mUPC(0), mHealth(100), mSquareMetersCredit(0), mSquareMetersCommitted(0), @@ -147,6 +166,11 @@ mRegionCrossingSlot(), mNavMeshSlot(), mIsNavMeshDirty(false) { + mUPCSupported = gHippoGridManager->getConnectedGrid()->getUPCSupported(); + + if (mUPCSupported) + gGenericDispatcher.addHandler("upcbalance", &sDispatchUPCBalance); + // status bar can possible overlay menus? setMouseOpaque(FALSE); setIsChrome(TRUE); @@ -168,10 +192,14 @@ mIsNavMeshDirty(false) mTextParcelName = getChild("ParcelNameText" ); mTextBalance = getChild("BalanceText" ); + mTextUPC = getChild("UPCText" ); mTextHealth = getChild("HealthText" ); mTextTime = getChild("TimeText" ); + if (!mUPCSupported) + mTextUPC->setVisible(false); + childSetAction("scriptout", onClickScriptDebug, this); childSetAction("health", onClickHealth, this); childSetAction("no_fly", onClickFly, this); @@ -595,6 +623,14 @@ void LLStatusBar::refresh() } // Set rects of money, buy money, time + if (mUPCSupported) + { + childGetRect("UPCText", r); + r.translate( new_right - r.mRight, 0); + childSetRect("UPCText", r); + new_right -= r.getWidth() - 18; + } + childGetRect("BalanceText", r); r.translate( new_right - r.mRight, 0); childSetRect("BalanceText", r); @@ -631,6 +667,8 @@ void LLStatusBar::refresh() void LLStatusBar::setVisibleForMouselook(bool visible) { mTextBalance->setVisible(visible); + if (mUPCSupported) + mTextUPC->setVisible(visible); mTextTime->setVisible(visible); childSetVisible("buycurrency", visible); childSetVisible("search_editor", visible); @@ -653,7 +691,7 @@ void LLStatusBar::creditBalance(S32 credit) void LLStatusBar::setBalance(S32 balance) { mTextBalance->setText(gHippoGridManager->getConnectedGrid()->getCurrencySymbol().c_str() + - LLResMgr::getInstance()->getMonetaryString(balance)); + LLResMgr::getInstance()->getMonetaryString(balance - mUPC)); if (mBalance && (fabs((F32)(mBalance - balance)) > gSavedSettings.getF32("UISndMoneyChangeThreshold"))) { @@ -670,6 +708,14 @@ void LLStatusBar::setBalance(S32 balance) } } +void LLStatusBar::setUPC(S32 upc) +{ + mTextUPC->setText("UPC " + LLResMgr::getInstance()->getMonetaryString(upc)); + + mUPC = upc; + + setBalance(mBalance); +} // static void LLStatusBar::sendMoneyBalanceRequest() diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h index 144047166..b24913ef3 100644 --- a/indra/newview/llstatusbar.h +++ b/indra/newview/llstatusbar.h @@ -61,6 +61,7 @@ public: // MANIPULATORS void setBalance(S32 balance); + void setUPC(S32 balance); void debitBalance(S32 debit); void creditBalance(S32 credit); @@ -99,6 +100,7 @@ private: private: LLTextBox *mTextBalance; + LLTextBox *mTextUPC; LLTextBox *mTextHealth; LLTextBox *mTextTime; @@ -110,6 +112,7 @@ private: LLButton *mBtnBuyCurrency; S32 mBalance; + S32 mUPC; S32 mHealth; S32 mSquareMetersCredit; S32 mSquareMetersCommitted; @@ -118,6 +121,7 @@ private: boost::signals2::connection mRegionCrossingSlot; LLPathfindingNavMesh::navmesh_slot_t mNavMeshSlot; bool mIsNavMeshDirty; + bool mUPCSupported; static std::vector sDays; static std::vector sMonths; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 289d207b8..56a6267b2 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -36,7 +36,7 @@ #include "lltexturefetch.h" -#include "llcurl.h" +#include "aicurl.h" #include "lldir.h" #include "llhttpclient.h" #include "llhttpstatuscodes.h" @@ -60,6 +60,7 @@ #include "llstartup.h" #include "llsdserialize.h" #include "llbuffer.h" +#include "hippogridmanager.h" class AIHTTPTimeoutPolicy; extern AIHTTPTimeoutPolicy HTTPGetResponder_timeout; @@ -253,6 +254,7 @@ private: LLUUID mID; LLHost mHost; std::string mUrl; + AIPerServicePtr mPerServicePtr; // Pointer to the AIPerService corresponding to the host of mUrl. U8 mType; F32 mImagePriority; U32 mWorkPriority; @@ -495,30 +497,6 @@ public: SGHostBlackList::blacklist_t SGHostBlackList::blacklist; -#if 0 -//call every time a connection is opened -//return true if connecting allowed -static bool sgConnectionThrottle() { - const U32 THROTTLE_TIMESTEPS_PER_SECOND = 10; - static const LLCachedControl max_connections_per_second("HTTPRequestRate", 30); - U32 max_connections = max_connections_per_second/THROTTLE_TIMESTEPS_PER_SECOND; - const U32 timestep = USEC_PER_SEC/THROTTLE_TIMESTEPS_PER_SECOND; - U64 now = LLTimer::getTotalTime(); - std::deque timestamps; - while(!timestamps.empty() && (timestamps[0]<=now-timestep)) { - timestamps.pop_front(); - } - if(timestamps.size() < max_connections) { - //llinfos << "throttle pass" << llendl; - timestamps.push_back(now); - return true; - } else { - //llinfos << "throttle fail" << llendl; - return false; - } -} -#endif - ////////////////////////////////////////////////////////////////////////////// // Cross-thread messaging for asset metrics. @@ -819,6 +797,17 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, { mCanUseNET = mUrl.empty() ; + if (!mCanUseNET) + { + // Probably a file://, but well; in that case servicename will be empty. + std::string servicename = AIPerService::extract_canonical_servicename(mUrl); + if (!servicename.empty()) + { + // Make sure mPerServicePtr is up to date with mUrl. + mPerServicePtr = AIPerService::instance(servicename); + } + } + calcWorkPriority(); mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; //llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << " URL:"<< mUrl << llendl; @@ -1174,6 +1163,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { mUrl = http_url + "/?texture_id=" + mID.asString().c_str(); mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id. + mPerServicePtr = AIPerService::instance(AIPerService::extract_canonical_servicename(http_url)); } else { @@ -1182,7 +1172,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - // This will happen if not logged in or if a region deoes not have HTTP Texture enabled + // This will happen if not logged in or if a region does not have HTTP Texture enabled //llwarns << "Region not found for host: " << mHost << llendl; mCanUseHTTP = false; } @@ -1259,29 +1249,14 @@ bool LLTextureFetchWorker::doWork(S32 param) { if(mCanUseHTTP) { - //NOTE: - //control the number of the http requests issued for: - //1, not opening too many file descriptors at the same time; - //2, control the traffic of http so udp gets bandwidth. - // - static const LLCachedControl max_http_requests("HTTPMaxRequests", 8); - static const LLCachedControl min_http_requests("HTTPMinRequests", 2); - static const LLCachedControl throttle_bandwidth("HTTPThrottleBandwidth", 2000); - if(((U32)mFetcher->getNumHTTPRequests() >= max_http_requests) || - ((mFetcher->getTextureBandwidth() > throttle_bandwidth) && - ((U32)mFetcher->getNumHTTPRequests() > min_http_requests))) - { - return false ; //wait. - } - - mFetcher->removeFromNetworkQueue(this, false); - S32 cur_size = 0; if (mFormattedImage.notNull()) { cur_size = mFormattedImage->getDataSize(); // amount of data we already have if (mFormattedImage->getDiscardLevel() == 0) { + // Already have all data. + mFetcher->removeFromNetworkQueue(this, false); // Note sure this is necessary, but it's what the old did --Aleric if(cur_size > 0) { // We already have all the data, just decode it @@ -1295,20 +1270,30 @@ bool LLTextureFetchWorker::doWork(S32 param) } } } + + // Let AICurl decide if we can process more HTTP requests at the moment or not. + if (!AIPerService::wantsMoreHTTPRequestsFor(mPerServicePtr)) + { + return false ; //wait. + } + // If AIPerService::wantsMoreHTTPRequestsFor returns true then it approved ONE request. + // This object keeps track of whether or not that is honored. + AIPerService::Approvement approvement(mPerServicePtr); + + mFetcher->removeFromNetworkQueue(this, false); + mRequestedSize = mDesiredSize - cur_size; mRequestedDiscard = mDesiredDiscard; mRequestedOffset = cur_size; - + bool res = false; if (!mUrl.empty()) { mLoaded = FALSE; mGetStatus = 0; mGetReason.clear(); - static const LLCachedControl throttle_bandwidth("HTTPThrottleBandwidth", 2000); LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset << " Bytes: " << mRequestedSize - << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << throttle_bandwidth << LL_ENDL; setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mState = WAIT_HTTP_REQ; @@ -1339,7 +1324,9 @@ bool LLTextureFetchWorker::doWork(S32 param) } LLHTTPClient::request(mUrl, LLHTTPClient::HTTP_GET, NULL, new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, mRequestedOffset, true), - headers/*,*/ DEBUG_CURLIO_PARAM(debug_off), keep_alive, no_does_authentication, allow_compressed_reply, NULL, 0, NULL); + headers/*,*/ DEBUG_CURLIO_PARAM(debug_off), keep_alive, no_does_authentication, allow_compressed_reply, NULL, 0, NULL, false); + // Now the request was added to the command queue. + approvement.honored(); res = true; } if (!res) @@ -2050,8 +2037,6 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mBadPacketCount(0), mTextureCache(cache), mImageDecodeThread(imagedecodethread), - mTextureBandwidth(0), - mHTTPTextureBits(0), mTotalHTTPRequests(0), mQAMode(qa_mode), mTotalCacheReadCount(0U), @@ -2203,7 +2188,6 @@ void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) { LLMutexLock lock(&mNetworkQueueMutex); mHTTPTextureQueue.erase(id); - mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits } void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) @@ -2261,15 +2245,6 @@ S32 LLTextureFetch::getNumRequests() return size ; } -S32 LLTextureFetch::getNumHTTPRequests() -{ - mNetworkQueueMutex.lock() ; - S32 size = (S32)mHTTPTextureQueue.size(); - mNetworkQueueMutex.unlock() ; - - return size ; -} - U32 LLTextureFetch::getTotalNumHTTPRequests() { mNetworkQueueMutex.lock() ; @@ -2425,15 +2400,6 @@ void LLTextureFetch::commonUpdate() //virtual S32 LLTextureFetch::update(F32 max_time_ms) { - { - mNetworkQueueMutex.lock() ; - - gTextureList.sTextureBits += mHTTPTextureBits ; - mHTTPTextureBits = 0 ; - - mNetworkQueueMutex.unlock() ; - } - S32 res = LLWorkerThread::update(max_time_ms); if (!mDebugPause) diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index f23961083..4062c310a 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -79,16 +79,12 @@ public: bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data); bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data); - void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; } - F32 getTextureBandwidth() { return mTextureBandwidth; } - // Debug BOOL isFromLocalCache(const LLUUID& id); S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http); void dump(); S32 getNumRequests() ; - S32 getNumHTTPRequests() ; U32 getTotalNumHTTPRequests() ; // Public for access by callbacks @@ -165,11 +161,8 @@ private: queue_t mHTTPTextureQueue; typedef std::map > cancel_queue_t; cancel_queue_t mCancelQueue; - F32 mTextureBandwidth; LLTextureInfo mTextureInfo; - U32 mHTTPTextureBits; - //debug use U32 mTotalHTTPRequests ; diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index e57fbc3a2..41c819b5f 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -37,6 +37,7 @@ #include "llimageworker.h" #include "llrender.h" +#include "aicurlperservice.h" #include "llappviewer.h" #include "llselectmgr.h" #include "llviewertexlayer.h" @@ -64,6 +65,15 @@ LLTextureSizeView *gTextureCategoryView = NULL; //static std::set LLTextureView::sDebugImages; +// Forward declaration. +namespace AICurlInterface { + U32 getNumHTTPCommands(void); + U32 getNumHTTPQueued(void); + U32 getNumHTTPAdded(void); + U32 getNumHTTPRunning(void); + size_t getHTTPBandwidth(void); +} // namespace AICurlInterface + //////////////////////////////////////////////////////////////////////////// static std::string title_string1a("Tex UUID Area DDis(Req) DecodePri(Fetch) [download] pk/max"); @@ -593,7 +603,7 @@ void LLGLTexMemBar::draw() #endif //---------------------------------------------------------------------------- - text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d IW:%d RAW:%d(%d) HTP:%d DEC:%d CRE:%d ", + text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d IW:%d RAW:%d(%d) HTTP:%d/%d/%d/%d DEC:%d CRE:%d ", gTextureList.getNumImages(), LLAppViewer::getTextureFetch()->getNumRequests(), LLAppViewer::getTextureFetch()->getNumDeletes(), LLAppViewer::getTextureFetch()->mPacketCount, LLAppViewer::getTextureFetch()->mBadPacketCount, @@ -601,7 +611,10 @@ void LLGLTexMemBar::draw() LLLFSThread::sLocal->getPending(), LLAppViewer::getImageDecodeThread()->getPending(), LLImageRaw::sRawImageCount, LLImageRaw::sRawImageCachedCount, - LLAppViewer::getTextureFetch()->getNumHTTPRequests(), + AICurlInterface::getNumHTTPCommands(), + AICurlInterface::getNumHTTPQueued(), + AICurlInterface::getNumHTTPAdded(), + AICurlInterface::getNumHTTPRunning(), LLAppViewer::getImageDecodeThread()->getPending(), gTextureList.mCreateTextureList.size()); @@ -609,14 +622,15 @@ void LLGLTexMemBar::draw() text_color, LLFontGL::LEFT, LLFontGL::TOP); left += LLFontGL::getFontMonospace()->getWidth(text); - F32 bandwidth = LLAppViewer::getTextureFetch()->getTextureBandwidth(); - F32 max_bandwidth = gSavedSettings.getF32("HTTPThrottleBandwidth"); - color = bandwidth > max_bandwidth ? LLColor4::red : bandwidth > max_bandwidth*.75f ? LLColor4::yellow : text_color; + // This bandwidth is averaged over 1 seconds (in bytes/s). + size_t const bandwidth = AICurlInterface::getHTTPBandwidth(); + size_t const max_bandwidth = AIPerService::getHTTPThrottleBandwidth125(); + color = (bandwidth > max_bandwidth) ? LLColor4::red : ((bandwidth > max_bandwidth * .75f) ? LLColor4::yellow : text_color); color[VALPHA] = text_color[VALPHA]; - text = llformat("BW:%.0f/%.0f",bandwidth, max_bandwidth); + text = llformat("BW:%lu/%lu", bandwidth / 125, max_bandwidth / 125); LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, v_offset + line_height*2, color, LLFontGL::LEFT, LLFontGL::TOP); - + S32 dx1 = 0; if (LLAppViewer::getTextureFetch()->mDebugPause) { diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 9e30efa3c..59958523b 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -84,7 +84,6 @@ #include "aicurl.h" #include "aihttptimeoutpolicy.h" - #ifdef TOGGLE_HACKED_GODLIKE_VIEWER BOOL gHackGodmode = FALSE; #endif @@ -330,6 +329,12 @@ static bool handleBandwidthChanged(const LLSD& newvalue) return true; } +static bool handleHTTPBandwidthChanged(const LLSD& newvalue) +{ + AIPerService::setHTTPThrottleBandwidth((F32) newvalue.asReal()); + return true; +} + static bool handleChatFontSizeChanged(const LLSD& newvalue) { if(gConsole) @@ -666,6 +671,7 @@ void settings_setup_listeners() gSavedSettings.getControl("RenderTreeLODFactor")->getSignal()->connect(boost::bind(&handleTreeLODChanged, _2)); gSavedSettings.getControl("RenderFlexTimeFactor")->getSignal()->connect(boost::bind(&handleFlexLODChanged, _2)); gSavedSettings.getControl("ThrottleBandwidthKBPS")->getSignal()->connect(boost::bind(&handleBandwidthChanged, _2)); + gSavedSettings.getControl("HTTPThrottleBandwidth")->getSignal()->connect(boost::bind(&handleHTTPBandwidthChanged, _2)); gSavedSettings.getControl("RenderGamma")->getSignal()->connect(boost::bind(&handleGammaChanged, _2)); gSavedSettings.getControl("RenderFogRatio")->getSignal()->connect(boost::bind(&handleFogRatioChanged, _2)); gSavedSettings.getControl("RenderMaxPartCount")->getSignal()->connect(boost::bind(&handleMaxPartCountChanged, _2)); @@ -792,7 +798,7 @@ void settings_setup_listeners() gSavedSettings.getControl("AscentAvatarZModifier")->getSignal()->connect(boost::bind(&handleAscentAvatarModifier, _2)); gSavedSettings.getControl("CurlMaxTotalConcurrentConnections")->getSignal()->connect(boost::bind(&AICurlInterface::handleCurlMaxTotalConcurrentConnections, _2)); - gSavedSettings.getControl("CurlConcurrentConnectionsPerHost")->getSignal()->connect(boost::bind(&AICurlInterface::handleCurlConcurrentConnectionsPerHost, _2)); + gSavedSettings.getControl("CurlConcurrentConnectionsPerService")->getSignal()->connect(boost::bind(&AICurlInterface::handleCurlConcurrentConnectionsPerService, _2)); gSavedSettings.getControl("NoVerifySSLCert")->getSignal()->connect(boost::bind(&AICurlInterface::handleNoVerifySSLCert, _2)); gSavedSettings.getControl("CurlTimeoutDNSLookup")->getValidateSignal()->connect(boost::bind(&validateCurlTimeoutDNSLookup, _2)); diff --git a/indra/newview/llviewergenericmessage.cpp b/indra/newview/llviewergenericmessage.cpp index 312fae50a..c4675e7bb 100644 --- a/indra/newview/llviewergenericmessage.cpp +++ b/indra/newview/llviewergenericmessage.cpp @@ -105,7 +105,7 @@ void process_generic_message(LLMessageSystem* msg, void**) } else if (agent_id != gAgent.getID()) { - llwarns << "GenericMessage for wrong agent" << llendl; + llwarns << "GenericMessage for wrong agent " << agent_id << llendl; return; } else diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index eb3e35997..165e22201 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -353,22 +353,25 @@ void LLViewerInventoryItem::fetchFromServer(void) const { std::string url; - LLViewerRegion* region = gAgent.getRegion(); - // we have to check region. It can be null after region was destroyed. See EXT-245 - if (region) + if (gSavedSettings.getBOOL("UseHTTPInventory")) { - if(gAgent.getID() != mPermissions.getOwner()) - { - url = region->getCapability("FetchLib2"); - } - else - { - url = region->getCapability("FetchInventory2"); - } - } - else - { - llwarns << "Agent Region is absent" << llendl; + LLViewerRegion* region = gAgent.getRegion(); + // we have to check region. It can be null after region was destroyed. See EXT-245 + if (region) + { + if(gAgent.getID() != mPermissions.getOwner()) + { + url = region->getCapability("FetchLib2"); + } + else + { + url = region->getCapability("FetchInventory2"); + } + } + else + { + llwarns << "Agent Region is absent" << llendl; + } } if (!url.empty()) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 18aa99ed1..ba8a0d820 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -63,7 +63,6 @@ #include "llfirstuse.h" #include "llfloaterabout.h" #include "llfloateractivespeakers.h" -#include "llfloateranimpreview.h" #include "llfloateravatarinfo.h" #include "llfloateravatarlist.h" #include "llfloateravatartextures.h" @@ -358,7 +357,6 @@ void handle_singleton_toggle(void *) // void handle_fake_away_status(void*); -void handle_area_search(void*); // for pose stand LLUUID current_pose = LLUUID::null; @@ -404,13 +402,8 @@ BOOL handle_check_pose(void* userdata) { } -void handle_force_ground_sit(void*); -void handle_hide_typing_notification(void*); void handle_close_all_notifications(void*); void handle_open_message_log(void*); -void handle_edit_ao(void*); -void handle_sounds_explorer(void*); -void handle_blacklist(void*); // void handle_reset_view(); @@ -517,6 +510,12 @@ void region_change(); void parse_simulator_features(); void custom_selected(void* user_data); +void reset_vertex_buffers(void *user_data) +{ + gPipeline.clearRebuildGroups(); + gPipeline.resetVertexBuffers(); +} + class LLMenuParcelObserver : public LLParcelObserver { public: @@ -700,32 +699,6 @@ void init_menus() // TomY TODO convert these two LLMenuGL*menu; - menu = new LLMenuGL("Singularity"); - menu->setCanTearOff(TRUE); - menu->addChild(new LLMenuItemCallGL( "Close All Dialogs", - &handle_close_all_notifications, NULL, NULL, 'D', MASK_CONTROL | MASK_ALT | MASK_SHIFT)); - menu->addSeparator(); - menu->addChild(new LLMenuItemCallGL( "Fake Away Status", &handle_fake_away_status, NULL)); - menu->addChild(new LLMenuItemCallGL( "Force Ground Sit", &handle_force_ground_sit, NULL)); - - menu->addSeparator(); - menu->addChild(new LLMenuItemCallGL( "Animation Override...", - &handle_edit_ao, NULL)); - menu->addChild(new LLMenuItemCheckGL( "Nimble", - &menu_toggle_control, - NULL, - &menu_check_control, - (void*)"Nimble")); - menu->addSeparator(); - menu->addChild(new LLMenuItemCallGL( "Object Area Search", &handle_area_search, NULL)); - - menu->addChild(new LLMenuItemCallGL( "Sound Explorer", - &handle_sounds_explorer, NULL)); - menu->addChild(new LLMenuItemCallGL( "Asset Blacklist", - &handle_blacklist, NULL)); - menu->addChild(new LLMenuItemCheckGL( "Streaming Audio Display", - &handle_ticker_toggle, &handle_ticker_enabled, &handle_singleton_check, NULL )); - // // Add in the pose stand ------------------------------------------- /*LLMenuGL* sub = new LLMenuGL("Pose Stand..."); @@ -740,12 +713,6 @@ void init_menus() sub->addChild(new LLMenuItemCallGL( "Stop Pose Stand", &handle_pose_stand_stop, NULL)); // ------------------------------------------------------*/ - menu->addChild(new LLMenuItemCheckGL("Pose Stand",&handle_toggle_pose, NULL, &handle_check_pose, NULL)); - - //these should always be last in a sub menu - menu->createJumpKeys(); - gMenuBarView->addChild( menu ); - menu = new LLMenuGL(CLIENT_MENU_NAME); menu->setCanTearOff(TRUE); init_client_menu(menu); @@ -793,8 +760,10 @@ void init_menus() ins->setVisible(false); ins = gMenuBarView->getChildView("insert_tools", true, false); ins->setVisible(false); - /* Singu Note: When the advanced menu is made xml, this should be uncommented. + /* Singu Note: When the advanced and/or admin menus are made xml, these should be uncommented. ins = gMenuBarView->getChildView("insert_advanced", true, false); + ins->setVisible(false); + ins = gMenuBarView->getChildView("insert_admin", true, false); ins->setVisible(false);*/ LLEnvManagerNew::instance().setRegionChangeCallback(®ion_change); @@ -1483,6 +1452,8 @@ void init_debug_rendering_menu(LLMenuGL* menu) item = new LLMenuItemCheckGL("Aggressive Alpha Masking", menu_toggle_control, NULL, menu_check_control, (void*)"SHUseRMSEAutoMask"); menu->addChild(item); + menu->addChild(new LLMenuItemCallGL("Rebuild Vertex Buffers", reset_vertex_buffers, NULL, NULL, 'V', MASK_CONTROL | MASK_SHIFT)); + item = new LLMenuItemCheckGL("Animate Textures", menu_toggle_control, NULL, menu_check_control, (void*)"AnimateTextures"); menu->addChild(item); @@ -1578,7 +1549,7 @@ void init_debug_avatar_menu(LLMenuGL* menu) (void*)LLPipeline::RENDER_DEBUG_ATTACHMENT_BYTES)); menu->addChild(new LLMenuItemToggleGL( "Debug Rotation", &LLVOAvatar::sDebugAvatarRotation)); menu->addChild(new LLMenuItemCallGL("Dump Attachments", handle_dump_attachments)); - menu->addChild(new LLMenuItemCallGL("Rebake Textures", handle_rebake_textures, NULL, NULL, 'R', MASK_ALT | MASK_CONTROL )); + menu->addChild(new LLMenuItemCallGL("Rebake Textures", handle_rebake_textures)); #ifndef LL_RELEASE_FOR_DOWNLOAD menu->addChild(new LLMenuItemCallGL("Debug Avatar Textures", handle_debug_avatar_textures, NULL, NULL, 'A', MASK_SHIFT|MASK_CONTROL|MASK_ALT)); menu->addChild(new LLMenuItemCallGL("Dump Local Textures", handle_dump_avatar_local_textures, NULL, NULL, 'M', MASK_SHIFT|MASK_ALT )); @@ -1703,6 +1674,12 @@ void init_server_menu(LLMenuGL* menu) menu->addChild(new LLMenuItemCallGL( "God Tools...", &LLFloaterGodTools::show, &enable_god_basic, NULL)); + { + LLMenuItemCallGL* item = new LLMenuItemCallGL("insert_admin", NULL); + item->setVisible(false); + menu->addChild(item); + } + menu->addSeparator(); menu->addChild(new LLMenuItemCallGL("Save Region State", @@ -3720,21 +3697,6 @@ void handle_open_message_log(void*) LLFloaterMessageLog::show(); } -void handle_edit_ao(void*) -{ - LLFloaterAO::show(NULL); -} - -void handle_sounds_explorer(void*) -{ - LLFloaterExploreSounds::toggle(); -} - -void handle_blacklist(void*) -{ - LLFloaterBlacklist::show(); -} - void handle_close_all_notifications(void*) { LLView::child_list_t child_list(*(gNotifyBoxView->getChildList())); @@ -3758,21 +3720,6 @@ void handle_fake_away_status(void*) gSavedSettings.setBOOL("FakeAway", !fake_away); } -void handle_force_ground_sit(void*) -{ - if (isAgentAvatarValid()) - { - if (!gAgentAvatarp->isSitting()) - { - gAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND); - } - else - { - gAgent.setControlFlags(AGENT_CONTROL_STAND_UP); - } - } -} - // /* @@ -6685,6 +6632,14 @@ class LLFloaterVisible : public view_listener_t { new_value = LLFloaterPathfindingCharacters::instanceVisible(LLSD()); } + else if (floater_name == "sound_explorer") + { + new_value = LLFloaterExploreSounds::visible(); + } + else if (floater_name == "asset_blacklist") + { + new_value = LLFloaterBlacklist::visible(); + } gMenuHolder->findControl(control_name)->setValue(new_value); return true; } @@ -9250,6 +9205,153 @@ class LLWorldDayCycle : public view_listener_t } }; + +class SinguCloseAllDialogs : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + handle_close_all_notifications(NULL); + return true; + } +}; + +class SinguAnimationOverride : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLFloaterAO::show(NULL); + + return true; + } +}; + +class SinguNimble : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + gSavedSettings.setBOOL("Nimble", !gSavedSettings.getBOOL("Nimble")); + + return true; + } +}; + +class SinguCheckNimble : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + gMenuHolder->findControl(userdata["control"].asString())->setValue(gSavedSettings.getBOOL("Nimble")); + + return true; + } +}; + +class SinguSoundExplorer : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLFloaterExploreSounds::toggle(); + + return true; + } +}; + +class SinguAssetBlacklist : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLFloaterBlacklist::toggle(); + + return true; + } +}; + +class SinguStreamingAudioDisplay : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + handle_ticker_toggle(NULL); + + return true; + } +}; + +class SinguCheckStreamingAudioDisplay : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + gMenuHolder->findControl(userdata["control"].asString())->setValue(handle_singleton_check(NULL)); + + return true; + } +}; + +class SinguEnableStreamingAudioDisplay : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + return handle_ticker_enabled(NULL); + } +}; + +class SinguPoseStand : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + handle_toggle_pose(NULL); + + return true; + } +}; + +class SinguCheckPoseStand : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + gMenuHolder->findControl(userdata["control"].asString())->setValue(handle_check_pose(NULL)); + return true; + } +}; + +class SinguRebake : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + handle_rebake_textures(NULL); + return true; + } +}; + +class SinguDebugConsole : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + handle_singleton_toggle(NULL); + return true; + } +}; + +class SinguCheckDebugConsole : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + gMenuHolder->findControl(userdata["control"].asString())->setValue(handle_singleton_check(NULL)); + return true; + } +}; + +class SinguVisibleDebugConsole : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + if (LLViewerRegion* region = gAgent.getRegion()) + { + if (LLView* item = gMenuBarView->getChildView("Region Debug Console", true, false)) + item->setVisible(!(region->getCapability("SimConsoleAsync").empty() || region->getCapability("SimConsole").empty())); + } + return true; + } +}; + void addMenu(view_listener_t *menu, const std::string& name) { sMenus.push_back(menu); @@ -9525,6 +9627,24 @@ void initialize_menus() addMenu(new LLEditableSelected(), "EditableSelected"); addMenu(new LLEditableSelectedMono(), "EditableSelectedMono"); + // Singularity menu + addMenu(new SinguCloseAllDialogs(), "CloseAllDialogs"); + // ---- Fake away handled elsewhere + addMenu(new SinguAnimationOverride(), "AnimationOverride"); + addMenu(new SinguNimble(), "Nimble"); + addMenu(new SinguCheckNimble(), "CheckNimble"); + addMenu(new SinguSoundExplorer(), "SoundExplorer"); + addMenu(new SinguAssetBlacklist(), "AssetBlacklist"); + addMenu(new SinguStreamingAudioDisplay(), "StreamingAudioDisplay"); + addMenu(new SinguEnableStreamingAudioDisplay(), "EnableStreamingAudioDisplay"); + addMenu(new SinguCheckStreamingAudioDisplay(), "CheckStreamingAudioDisplay"); + addMenu(new SinguPoseStand(), "PoseStand"); + addMenu(new SinguCheckPoseStand(), "CheckPoseStand"); + addMenu(new SinguRebake(), "Rebake"); + addMenu(new SinguDebugConsole(), "RegionDebugConsole"); + addMenu(new SinguCheckDebugConsole(), "CheckRegionDebugConsole"); + addMenu(new SinguVisibleDebugConsole(), "VisibleRegionDebugConsole"); + // [RLVa:KB] - Checked: 2010-01-18 (RLVa-1.1.0m) | Added: RLVa-1.1.0m | OK if (rlv_handler_t::isEnabled()) { diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index a111d95de..7492ea725 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -38,6 +38,7 @@ #include "llagentcamera.h" #include "statemachine/aifilepicker.h" #include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llfloaterimagepreview.h" #include "llfloatermodelpreview.h" #include "llfloaternamedesc.h" @@ -348,8 +349,17 @@ class LLFileUploadAnim : public view_listener_t, public AIFileUpload // Inherited from AIFileUpload. /*virtual*/ void handle_event(std::string const& filename) { - LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename); - LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_animation_preview.xml"); + int len = filename.size(); + if (len >= 5 && filename.substr(len - 5, 5) == ".anim") + { + LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename); + LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_animation_anim_preview.xml"); + floaterp->childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%s%d", gHippoGridManager->getConnectedGrid()->getCurrencySymbol().c_str(), LLGlobalEconomy::Singleton::getInstance()->getPriceUpload())); + } + else + { + LLUICtrlFactory::getInstance()->buildFloater(new LLFloaterBvhPreview(filename), "floater_animation_bvh_preview.xml"); + } } }; @@ -871,8 +881,8 @@ void upload_new_resource(const std::string& src_filename, std::string name, } else if (exten == "bvh") { - error_message = llformat("We do not currently support bulk upload of animation files\n"); - upload_error(error_message, "DoNotSupportBulkAnimationUpload", filename, args); + error_message = llformat("We do not currently support bulk upload of BVH animation files\n"); + upload_error(error_message, "DoNotSupportBulkBVHAnimationUpload", filename, args); return; } // diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 7d3167fb3..6ca69fd0f 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -55,7 +55,7 @@ #include "llagentcamera.h" #include "llcallingcard.h" #include "llfirstuse.h" -#include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llfloaterbump.h" #include "llfloaterbuycurrency.h" #include "llfloaterbuyland.h" @@ -1239,6 +1239,7 @@ void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_nam } if (gSavedSettings.getBOOL("ShowInInventory") && + objects.size() == 1 && item != NULL && asset_type != LLAssetType::AT_CALLINGCARD && item->getInventoryType() != LLInventoryType::IT_ATTACHMENT && !from_name.empty()) @@ -6495,7 +6496,7 @@ void process_economy_data(LLMessageSystem *msg, void** /*user_data*/) LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL; LLFloaterImagePreview::setUploadAmount(upload_cost); - LLFloaterAnimPreview::setUploadAmount(upload_cost); + LLFloaterBvhPreview::setUploadAmount(upload_cost); std::string fee = gHippoGridManager->getConnectedGrid()->getUploadFee(); gMenuHolder->childSetLabelArg("Upload Image", "[UPLOADFEE]", fee); diff --git a/indra/newview/llviewerobjectbackup.cpp b/indra/newview/llviewerobjectbackup.cpp index 49d2e25bc..91c83f6ba 100644 --- a/indra/newview/llviewerobjectbackup.cpp +++ b/indra/newview/llviewerobjectbackup.cpp @@ -56,7 +56,7 @@ #include "llappviewer.h" #include "llassetuploadresponders.h" #include "statemachine/aifilepicker.h" -#include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llfloaterbuycurrency.h" #include "llfloaterimagepreview.h" #include "llfloaternamedesc.h" diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 4f67c2691..c5b6b98f6 100644 --- a/indra/newview/llviewerobjectlist.h +++ b/indra/newview/llviewerobjectlist.h @@ -39,6 +39,7 @@ // common includes #include "llstat.h" #include "llstring.h" +#include "sguuidhash.h" // project includes #include "llviewerobject.h" @@ -216,8 +217,8 @@ public: std::set mDeadObjects; - std::map > mUUIDObjectMap; - std::map > mUUIDAvatarMap; + boost::unordered_map > mUUIDObjectMap; + boost::unordered_map > mUUIDAvatarMap; //set of objects that need to update their cost std::set mStaleObjectCost; @@ -269,7 +270,7 @@ extern LLViewerObjectList gObjectList; // Inlines inline LLViewerObject *LLViewerObjectList::findObject(const LLUUID &id) const { - std::map >::const_iterator iter = mUUIDObjectMap.find(id); + boost::unordered_map >::const_iterator iter = mUUIDObjectMap.find(id); if(iter != mUUIDObjectMap.end()) { return iter->second; @@ -282,7 +283,7 @@ inline LLViewerObject *LLViewerObjectList::findObject(const LLUUID &id) const inline LLVOAvatar *LLViewerObjectList::findAvatar(const LLUUID &id) const { - std::map >::const_iterator iter = mUUIDAvatarMap.find(id); + boost::unordered_map >::const_iterator iter = mUUIDAvatarMap.find(id); return (iter != mUUIDAvatarMap.end()) ? iter->second.get() : NULL; } diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index c95d11314..f98c6f10f 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -104,7 +104,6 @@ #include "llsys.h" #include "llthread.h" #include "lltimer.h" -#include "lluuidhashmap.h" //#include "processor.h" #include "stdenums.h" #include "stdtypes.h" diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index f000bc39c..cfdf5ef43 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1597,8 +1597,9 @@ void LLViewerRegion::unpackRegionHandshake() // all of our terrain stuff, by if (compp->getParamsReady()) { - //this line creates frame stalls on region crossing and removing it appears to have no effect - //getLand().dirtyAllPatches(); + // The following line was commented out in http://hg.secondlife.com/viewer-development/commits/448b02f5b56f9e608952c810df5454f83051a992 + // by davep. However, this is needed to see changes in region/estate texture elevation ranges, and to update the terrain textures after terraforming. + getLand().dirtyAllPatches(); } else { @@ -1638,15 +1639,10 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("EnvironmentSettings"); capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); - - if (gSavedSettings.getBOOL("UseHTTPInventory")) //Caps suffixed with 2 by LL. Don't update until rest of fetch system is updated first. - { - capabilityNames.append("FetchLib2"); - capabilityNames.append("FetchLibDescendents2"); - capabilityNames.append("FetchInventory2"); - capabilityNames.append("FetchInventoryDescendents2"); - } - + capabilityNames.append("FetchLib2"); + capabilityNames.append("FetchLibDescendents2"); + capabilityNames.append("FetchInventory2"); + capabilityNames.append("FetchInventoryDescendents2"); capabilityNames.append("GamingData"); //Used by certain grids. capabilityNames.append("GetDisplayNames"); capabilityNames.append("GetMesh"); @@ -1701,6 +1697,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("ViewerMetrics"); capabilityNames.append("ViewerStartAuction"); capabilityNames.append("ViewerStats"); + capabilityNames.append("WearablesLoaded"); // Please add new capabilities alphabetically to reduce // merge conflicts. diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 2d20f8de3..4728f0ffd 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -67,6 +67,10 @@ class AIHTTPTimeoutPolicy; extern AIHTTPTimeoutPolicy viewerStatsResponder_timeout; +namespace AICurlInterface { +size_t getHTTPBandwidth(void); +} + class StatAttributes { public: @@ -210,7 +214,8 @@ LLViewerStats::LLViewerStats() : mLayersKBitStat("layerskbitstat"), mObjectKBitStat("objectkbitstat"), mAssetKBitStat("assetkbitstat"), - mTextureKBitStat("texturekbitstat"), + mHTTPTextureKBitStat("httptexturekbitstat"), + mUDPTextureKBitStat("udptexturekbitstat"), mMallocStat("mallocstat"), mVFSPendingOperations("vfspendingoperations"), mObjectsDrawnStat("objectsdrawnstat"), @@ -300,7 +305,8 @@ void LLViewerStats::resetStats() stats.mKBitStat.reset(); stats.mLayersKBitStat.reset(); stats.mObjectKBitStat.reset(); - stats.mTextureKBitStat.reset(); + stats.mHTTPTextureKBitStat.reset(); + stats.mUDPTextureKBitStat.reset(); stats.mVFSPendingOperations.reset(); stats.mAssetKBitStat.reset(); stats.mPacketsInStat.reset(); @@ -673,13 +679,13 @@ void update_statistics() // Only update texture stats periodically so that they are less noisy { - static const F32 texture_stats_freq = 10.f; + static const F32 texture_stats_freq = 0.25f; static LLFrameTimer texture_stats_timer; if (texture_stats_timer.getElapsedTimeF32() >= texture_stats_freq) { - stats.mTextureKBitStat.addValue(LLViewerTextureList::sTextureBits/1024.f); + stats.mHTTPTextureKBitStat.addValue(AICurlInterface::getHTTPBandwidth()/125.f); + stats.mUDPTextureKBitStat.addValue(LLViewerTextureList::sTextureBits/1024.f); stats.mTexturePacketsStat.addValue(LLViewerTextureList::sTexturePackets); - LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerTextureList::sTextureBits/1024.f/texture_stats_timer.getElapsedTimeF32()); gTotalTextureBytes += LLViewerTextureList::sTextureBits / 8; LLViewerTextureList::sTextureBits = 0; LLViewerTextureList::sTexturePackets = 0; diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index f7c7c0c1c..f6300d1e2 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -43,7 +43,8 @@ public: mLayersKBitStat, mObjectKBitStat, mAssetKBitStat, - mTextureKBitStat, + mHTTPTextureKBitStat, + mUDPTextureKBitStat, mVFSPendingOperations, mObjectsDrawnStat, mObjectsCulledStat, diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 7a7b70f9b..80efedbed 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -663,13 +663,15 @@ static LLFastTimer::DeclareTimer FTM_IMAGE_MEDIA("Media"); void LLViewerTextureList::updateImages(F32 max_time) { static BOOL cleared = FALSE; - static const LLCachedControl hide_tp_screen("AscentDisableTeleportScreens",false); + //Singu note: Don't clear vbos on local tp until some serious geom rebuild bugs are stomped out. + //Currently it causes prims to fail to rebuild when they should be popping back into visiblity. + //static const LLCachedControl hide_tp_screen("AscentDisableTeleportScreens",false); //Can't check gTeleportDisplay due to a process_teleport_local(), which sets it to true for local teleports... so: // Do this case if IS teleporting but NOT local teleporting, AND either the TP screen is set to appear OR we just entered the sim (TELEPORT_START_ARRIVAL) LLAgent::ETeleportState state = gAgent.getTeleportState(); if(state != LLAgent::TELEPORT_NONE && state != LLAgent::TELEPORT_LOCAL && state != LLAgent::TELEPORT_PENDING && - (!hide_tp_screen || state == LLAgent::TELEPORT_START_ARRIVAL || state == LLAgent::TELEPORT_ARRIVING)) + (/*!hide_tp_screen ||*/ state == LLAgent::TELEPORT_START_ARRIVAL || state == LLAgent::TELEPORT_ARRIVING)) { if(!cleared) { @@ -685,8 +687,6 @@ void LLViewerTextureList::updateImages(F32 max_time) } cleared = FALSE; - LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerStats::getInstance()->mTextureKBitStat.getMeanPerSec()); - S32 global_raw_memory; { global_raw_memory = *AIAccess(LLImageRaw::sGlobalRawMemory); @@ -1340,6 +1340,7 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d { received_size = msg->getReceiveSize() ; } + // Only used for statistics and texture console. gTextureList.sTextureBits += received_size * 8; gTextureList.sTexturePackets++; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 760d0d884..b237c2b0a 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -2403,6 +2403,17 @@ BOOL LLPipeline::updateDrawableGeom(LLDrawable* drawablep, BOOL priority) BOOL update_complete = drawablep->updateGeometry(priority); if (update_complete && assertInitialized()) { + //Workaround for 'missing prims' until it's fixed upstream by LL. + //Sometimes clearing CLEAR_INVISIBLE and FORCE_INVISIBLE in LLPipeline::stateSort was too late. Do it here instead, before + //the rebuild state is picked up on and LLVolumeGeometryManager::rebuildGeom is called. + //If the FORCE_INVISIBLE isn't cleared before the rebuildGeom call, the geometry will NOT BE REBUILT! + if(drawablep->isState(LLDrawable::CLEAR_INVISIBLE)) + { + // clear invisible flag here to avoid single frame glitch + drawablep->clearState(LLDrawable::FORCE_INVISIBLE|LLDrawable::CLEAR_INVISIBLE); + return false; //Defer to next mBuildQ1 iteration + } + drawablep->setState(LLDrawable::BUILT); mGeometryChanges++; } diff --git a/indra/newview/sgmemstat.cpp b/indra/newview/sgmemstat.cpp index d8c4a3f5a..ca4ce0593 100644 --- a/indra/newview/sgmemstat.cpp +++ b/indra/newview/sgmemstat.cpp @@ -18,7 +18,7 @@ #include "llviewerprecompiledheaders.h" #include "sgmemstat.h" -#if (!(LL_LINUX || LL_USE_TCMALLOC)) +#if (!LL_LINUX && !LL_USE_TCMALLOC) bool SGMemStat::haveStat() { return false; } diff --git a/indra/newview/skins/default/xui/en-us/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/en-us/floater_animation_anim_preview.xml new file mode 100644 index 000000000..785a80334 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/floater_animation_anim_preview.xml @@ -0,0 +1,77 @@ + + + + Name: + + + + Description: + + +