diff options
56 files changed, 1692 insertions, 1228 deletions
diff --git a/autobuild.xml b/autobuild.xml index 5a5ffa2898..1a353fe2e9 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2195,9 +2195,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>archive</key> <map> <key>hash</key> - <string>5abf2d9c0b250821c59cc60cd94fd8af</string> + <string>8114c6a7e499ea20d325db0de08ce30a</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54840/510064/openjpeg-1.5.1.538970-darwin64-538970.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105469/923024/openjpeg-2.5.0.575496-darwin64-575496.tar.bz2</string> </map> <key>name</key> <string>darwin64</string> @@ -2219,9 +2219,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>archive</key> <map> <key>hash</key> - <string>222a406ecb4071a9cc9635353afa337e</string> + <string>edc9388870d951632a6d595792293e05</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54977/511775/openjpeg-1.5.1.538970-windows-538970.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105472/923036/openjpeg-2.5.0.575496-windows-575496.tar.bz2</string> </map> <key>name</key> <string>windows</string> @@ -2231,16 +2231,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>archive</key> <map> <key>hash</key> - <string>5b5c80807fa8161f3480be3d89fe9516</string> + <string>b95f0732f2388ebb0ddf33d4a30e0ff1</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54974/511767/openjpeg-1.5.1.538970-windows64-538970.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105471/923037/openjpeg-2.5.0.575496-windows64-575496.tar.bz2</string> </map> <key>name</key> <string>windows64</string> </map> </map> <key>version</key> - <string>1.5.1.538970</string> + <string>2.5.0.575496</string> </map> <key>openssl</key> <map> diff --git a/doc/contributions.txt b/doc/contributions.txt index 2c1e5487ce..0149dae3f2 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -1377,6 +1377,7 @@ Sovereign Engineer OPEN-343 SL-11625 BUG-229030 + SL-14696 SL-14705 SL-14706 SL-14707 @@ -1384,6 +1385,9 @@ Sovereign Engineer SL-14732 SL-15096 SL-16127 + SL-18249 + SL-18394 + SL-18412 SpacedOut Frye VWR-34 VWR-45 @@ -1646,6 +1650,7 @@ Zi Ree VWR-25588 STORM-1790 STORM-1842 + SL-18348 Zipherius Turas VWR-76 VWR-77 diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index ff705101de..04b4202ca3 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -52,7 +52,7 @@ if(WINDOWS) set(release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}") set(release_files - openjpeg.dll + openjp2.dll libapr-1.dll libaprutil-1.dll libapriconv-1.dll @@ -220,7 +220,7 @@ elseif(LINUX) libgobject-2.0.so libhunspell-1.3.so.0.0.0 libopenal.so - libopenjpeg.so + libopenjp2.so libuuid.so.16 libuuid.so.16.0.22 libfontconfig.so.1.8.0 diff --git a/indra/cmake/FindOpenJPEG.cmake b/indra/cmake/FindOpenJPEG.cmake index 949384eec4..2d4353b54f 100644 --- a/indra/cmake/FindOpenJPEG.cmake +++ b/indra/cmake/FindOpenJPEG.cmake @@ -14,9 +14,10 @@ FIND_PATH(OPENJPEG_INCLUDE_DIR openjpeg.h /usr/local/include /usr/include/openjpeg /usr/include +include/openjpeg ) -SET(OPENJPEG_NAMES ${OPENJPEG_NAMES} openjpeg) +SET(OPENJPEG_NAMES ${OPENJPEG_NAMES} openjp2) FIND_LIBRARY(OPENJPEG_LIBRARY NAMES ${OPENJPEG_NAMES} PATHS /usr/lib /usr/local/lib diff --git a/indra/cmake/OpenJPEG.cmake b/indra/cmake/OpenJPEG.cmake index bf0bde2ba7..a078c97cb8 100644 --- a/indra/cmake/OpenJPEG.cmake +++ b/indra/cmake/OpenJPEG.cmake @@ -8,15 +8,7 @@ if (USESYSTEMLIBS) include(FindOpenJPEG) else (USESYSTEMLIBS) use_prebuilt_binary(openjpeg) - - if(WINDOWS) - # Windows has differently named release and debug openjpeg(d) libs. - set(OPENJPEG_LIBRARIES - debug openjpegd - optimized openjpeg) - else(WINDOWS) - set(OPENJPEG_LIBRARIES openjpeg) - endif(WINDOWS) - - set(OPENJPEG_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/openjpeg) + + set(OPENJPEG_LIBRARIES openjp2) + set(OPENJPEG_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/openjpeg) endif (USESYSTEMLIBS) diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt index d7706e73b2..3957ede77f 100644 --- a/indra/integration_tests/llui_libtest/CMakeLists.txt +++ b/indra/integration_tests/llui_libtest/CMakeLists.txt @@ -99,14 +99,14 @@ if (WINDOWS) # Copy over OpenJPEG.dll # *NOTE: On Windows with VS2005, only the first comment prints set(OPENJPEG_RELEASE - "${ARCH_PREBUILT_DIRS_RELEASE}/openjpeg.dll") + "${ARCH_PREBUILT_DIRS_RELEASE}/openjp2.dll") add_custom_command( TARGET llui_libtest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${OPENJPEG_RELEASE} ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Copying OpenJPEG DLLs to binary directory" ) set(OPENJPEG_DEBUG - "${ARCH_PREBUILT_DIRS_DEBUG}/openjpegd.dll") + "${ARCH_PREBUILT_DIRS_DEBUG}/openjp2.dll") add_custom_command( TARGET llui_libtest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${OPENJPEG_DEBUG} ${CMAKE_CURRENT_BINARY_DIR} diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 14bfb98629..70d8dfc8b9 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -288,25 +288,15 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl return name; } +namespace +{ + #if LL_WINDOWS static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific -U32 cpp_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop, const std::string& name) +U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) { - // C++ exceptions were logged in toplevelTryWrapper, but not SEH - // log SEH exceptions here, to make sure it gets into bugsplat's - // report and because __try won't allow std::string operations - if (code != STATUS_MSC_EXCEPTION) - { - LL_WARNS() << "SEH crash in " << name << ", code: " << code << LL_ENDL; - } - // Handle bugsplat here, since GetExceptionInformation() can only be - // called from within filter for __except(filter), not from __except's {} - // Bugsplat should get all exceptions, C++ and SEH - LLApp::instance()->reportCrashToBugsplat(exception_infop); - - // Only convert non C++ exceptions. if (code == STATUS_MSC_EXCEPTION) { // C++ exception, go on @@ -319,28 +309,38 @@ U32 cpp_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop, } } -void LLCoros::sehHandle(const std::string& name, const LLCoros::callable_t& callable) +void sehandle(const LLCoros::callable_t& callable) { __try { - LLCoros::toplevelTryWrapper(name, callable); + callable(); } - __except (cpp_exception_filter(GetExceptionCode(), GetExceptionInformation(), name)) + __except (exception_filter(GetExceptionCode(), GetExceptionInformation())) { - // convert to C++ styled exception for handlers other than bugsplat + // convert to C++ styled exception // Note: it might be better to use _se_set_translator // if you want exception to inherit full callstack - // - // in case of bugsplat this will get to exceptionTerminateHandler and - // looks like fiber will terminate application after that char integer_string[512]; - sprintf(integer_string, "SEH crash in %s, code: %lu\n", name.c_str(), GetExceptionCode()); + sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); throw std::exception(integer_string); } } -#endif -void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& callable) +#else // ! LL_WINDOWS + +inline void sehandle(const LLCoros::callable_t& callable) +{ + callable(); +} + +#endif // ! LL_WINDOWS + +} // anonymous namespace + +// Top-level wrapper around caller's coroutine callable. +// Normally we like to pass strings and such by const reference -- but in this +// case, we WANT to copy both the name and the callable to our local stack! +void LLCoros::toplevel(std::string name, callable_t callable) { // keep the CoroData on this top-level function's stack frame CoroData corodata(name); @@ -350,12 +350,12 @@ void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& call // run the code the caller actually wants in the coroutine try { - callable(); + sehandle(callable); } catch (const Stop& exc) { LL_INFOS("LLCoros") << "coroutine " << name << " terminating because " - << exc.what() << LL_ENDL; + << exc.what() << LL_ENDL; } catch (const LLContinueError&) { @@ -366,36 +366,14 @@ void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& call } catch (...) { -#if LL_WINDOWS - // Any OTHER kind of uncaught exception will cause the viewer to - // crash, SEH handling should catch it and report to bugsplat. - LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name)); - // to not modify callstack - throw; -#else // Stash any OTHER kind of uncaught exception in the rethrow() queue // to be rethrown by the main fiber. LL_WARNS("LLCoros") << "Capturing uncaught exception in coroutine " << name << LL_ENDL; LLCoros::instance().saveException(name, std::current_exception()); -#endif } } -// Top-level wrapper around caller's coroutine callable. -// Normally we like to pass strings and such by const reference -- but in this -// case, we WANT to copy both the name and the callable to our local stack! -void LLCoros::toplevel(std::string name, callable_t callable) -{ -#if LL_WINDOWS - // Because SEH can's have unwinding, need to call a wrapper - // 'try' is inside SEH handling to not catch LLContinue - sehHandle(name, callable); -#else - toplevelTryWrapper(name, callable); -#endif -} - //static void LLCoros::checkStop() { diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index dbff921f16..966ce03296 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -307,11 +307,7 @@ public: private: std::string generateDistinctName(const std::string& prefix) const; - void toplevelTryWrapper(const std::string& name, const callable_t& callable); -#if LL_WINDOWS - void sehHandle(const std::string& name, const callable_t& callable); // calls toplevelTryWrapper -#endif - void toplevel(std::string name, callable_t callable); // calls sehHandle or toplevelTryWrapper + void toplevel(std::string name, callable_t callable); struct CoroData; static CoroData& get_CoroData(const std::string& caller); void saveException(const std::string& name, std::exception_ptr exc); diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index 8b4a0ee6d8..a510b73096 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -34,6 +34,9 @@ #include <iostream> #include "apr_base64.h" +#include <boost/iostreams/device/array.hpp> +#include <boost/iostreams/stream.hpp> + #ifdef LL_USESYSTEMLIBS # include <zlib.h> #else @@ -2128,7 +2131,9 @@ std::string zip_llsd(LLSD& data) { //copy result into output if (strm.avail_out >= CHUNK) { - free(output); + deflateEnd(&strm); + if(output) + free(output); LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL; return std::string(); } @@ -2151,7 +2156,9 @@ std::string zip_llsd(LLSD& data) } else { - free(output); + deflateEnd(&strm); + if(output) + free(output); LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL; return std::string(); } @@ -2162,7 +2169,8 @@ std::string zip_llsd(LLSD& data) std::string result((char*) output, size); deflateEnd(&strm); - free(output); + if(output) + free(output); return result; } @@ -2172,53 +2180,66 @@ std::string zip_llsd(LLSD& data) // and deserializes from that copy using LLSDSerialize LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, S32 size) { + std::unique_ptr<U8[]> in = std::unique_ptr<U8[]>(new(std::nothrow) U8[size]); + if (!in) + { + return ZR_MEM_ERROR; + } + is.read((char*) in.get(), size); + + return unzip_llsd(data, in.get(), size); +} + +LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32 size) +{ U8* result = NULL; U32 cur_size = 0; z_stream strm; - const U32 CHUNK = 65536; + constexpr U32 CHUNK = 1024 * 512; - U8 *in = new(std::nothrow) U8[size]; - if (!in) + static thread_local std::unique_ptr<U8[]> out; + if (!out) { - return ZR_MEM_ERROR; + out = std::unique_ptr<U8[]>(new(std::nothrow) U8[CHUNK]); } - is.read((char*) in, size); - - U8 out[CHUNK]; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = size; - strm.next_in = in; + strm.next_in = const_cast<U8*>(in); S32 ret = inflateInit(&strm); do { strm.avail_out = CHUNK; - strm.next_out = out; + strm.next_out = out.get(); ret = inflate(&strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR) + switch (ret) + { + case Z_NEED_DICT: + case Z_DATA_ERROR: { inflateEnd(&strm); free(result); - delete [] in; return ZR_DATA_ERROR; } - - switch (ret) + case Z_STREAM_ERROR: + case Z_BUF_ERROR: { - case Z_NEED_DICT: - ret = Z_DATA_ERROR; - case Z_DATA_ERROR: + inflateEnd(&strm); + free(result); + return ZR_BUFFER_ERROR; + } + case Z_MEM_ERROR: + { inflateEnd(&strm); free(result); - delete [] in; return ZR_MEM_ERROR; - break; + } } U32 have = CHUNK-strm.avail_out; @@ -2231,17 +2252,15 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, { free(result); } - delete[] in; return ZR_MEM_ERROR; } result = new_result; - memcpy(result+cur_size, out, have); + memcpy(result+cur_size, out.get(), have); cur_size += have; - } while (ret == Z_OK); + } while (ret == Z_OK && ret != Z_STREAM_END); inflateEnd(&strm); - delete [] in; if (ret != Z_STREAM_END) { @@ -2251,37 +2270,11 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, //result now points to the decompressed LLSD block { - std::istringstream istr; - // Since we are using this for meshes, data we are dealing with tend to be large. - // So string can potentially fail to allocate, make sure this won't cause problems - try - { - std::string res_str((char*)result, cur_size); - - std::string deprecated_header("<? LLSD/Binary ?>"); - - if (res_str.substr(0, deprecated_header.size()) == deprecated_header) - { - res_str = res_str.substr(deprecated_header.size() + 1, cur_size); - } - cur_size = res_str.size(); - - istr.str(res_str); - } -#ifdef LL_WINDOWS - catch (std::length_error) - { - free(result); - return ZR_SIZE_ERROR; - } -#endif - catch (std::bad_alloc&) - { - free(result); - return ZR_MEM_ERROR; - } + char* result_ptr = strip_deprecated_header((char*)result, cur_size); - if (!LLSDSerialize::fromBinary(data, istr, cur_size, UNZIP_LLSD_MAX_DEPTH)) + boost::iostreams::stream<boost::iostreams::array_source> istrm(result_ptr, cur_size); + + if (!LLSDSerialize::fromBinary(data, istrm, cur_size, UNZIP_LLSD_MAX_DEPTH)) { free(result); return ZR_PARSE_ERROR; @@ -2395,4 +2388,22 @@ U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32 return result; } +char* strip_deprecated_header(char* in, U32& cur_size, U32* header_size) +{ + const char* deprecated_header = "<? LLSD/Binary ?>"; + constexpr size_t deprecated_header_size = 17; + + if (cur_size > deprecated_header_size + && memcmp(in, deprecated_header, deprecated_header_size) == 0) + { + in = in + deprecated_header_size; + cur_size = cur_size - deprecated_header_size; + if (header_size) + { + *header_size = deprecated_header_size + 1; + } + } + + return in; +} diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h index d6079fd9fa..d33d2b6f34 100644 --- a/indra/llcommon/llsdserialize.h +++ b/indra/llcommon/llsdserialize.h @@ -858,9 +858,12 @@ public: ZR_SIZE_ERROR, ZR_DATA_ERROR, ZR_PARSE_ERROR, + ZR_BUFFER_ERROR, + ZR_VERSION_ERROR } EZipRresult; // return OK or reason for failure static EZipRresult unzip_llsd(LLSD& data, std::istream& is, S32 size); + static EZipRresult unzip_llsd(LLSD& data, const U8* in, S32 size); }; //dirty little zip functions -- yell at davep @@ -868,4 +871,7 @@ LL_COMMON_API std::string zip_llsd(LLSD& data); LL_COMMON_API U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize,std::istream& is, S32 size); + +// returns a pointer to the array or past the array if the deprecated header exists +LL_COMMON_API char* strip_deprecated_header(char* in, U32& cur_size, U32* header_size = nullptr); #endif // LL_LLSDSERIALIZE_H diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 925da5674b..12985c3c7f 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -29,40 +29,41 @@ // this is defined so that we get static linking. #include "openjpeg.h" +#include "event.h" +#include "cio.h" -#include "lltimer.h" -//#include "llmemory.h" +#define MAX_ENCODED_DISCARD_LEVELS 5 // Factory function: see declaration in llimagej2c.cpp LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl() { - return new LLImageJ2COJ(); + return new LLImageJ2COJ(); } std::string LLImageJ2COJ::getEngineInfo() const { #ifdef OPENJPEG_VERSION - return std::string("OpenJPEG: " OPENJPEG_VERSION ", Runtime: ") - + opj_version(); + return std::string("OpenJPEG: " OPENJPEG_VERSION ", Runtime: ") + + opj_version(); #else - return std::string("OpenJPEG runtime: ") + opj_version(); + return std::string("OpenJPEG runtime: ") + opj_version(); #endif } // Return string from message, eliminating final \n if present static std::string chomp(const char* msg) { - // stomp trailing \n - std::string message = msg; - if (!message.empty()) - { - size_t last = message.size() - 1; - if (message[last] == '\n') - { - message.resize( last ); - } - } - return message; + // stomp trailing \n + std::string message = msg; + if (!message.empty()) + { + size_t last = message.size() - 1; + if (message[last] == '\n') + { + message.resize(last); + } +} + return message; } /** @@ -70,419 +71,770 @@ sample error callback expecting a LLFILE* client object */ void error_callback(const char* msg, void*) { - LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL; + LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL; } /** sample warning callback expecting a LLFILE* client object */ void warning_callback(const char* msg, void*) { - LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL; + LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL; } /** sample debug callback expecting no client object */ void info_callback(const char* msg, void*) { - LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL; + LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL; } // Divide a by 2 to the power of b and round upwards int ceildivpow2(int a, int b) { - return (a + (1 << b) - 1) >> b; + return (a + (1 << b) - 1) >> b; } - -LLImageJ2COJ::LLImageJ2COJ() - : LLImageJ2CImpl() +class JPEG2KBase { -} +public: + JPEG2KBase() {} + U8* buffer = nullptr; + OPJ_SIZE_T size = 0; + OPJ_OFF_T offset = 0; +}; -LLImageJ2COJ::~LLImageJ2COJ() +#define WANT_VERBOSE_OPJ_SPAM LL_DEBUG + +static void opj_info(const char* msg, void* user_data) { + llassert(user_data); +#if WANT_VERBOSE_OPJ_SPAM + LL_INFOS("OpenJPEG") << msg << LL_ENDL; +#endif } -bool LLImageJ2COJ::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region) +static void opj_warn(const char* msg, void* user_data) { - // No specific implementation for this method in the OpenJpeg case - return false; + llassert(user_data); +#if WANT_VERBOSE_OPJ_SPAM + LL_WARNS("OpenJPEG") << msg << LL_ENDL; +#endif } -bool LLImageJ2COJ::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels) +static void opj_error(const char* msg, void* user_data) { - // No specific implementation for this method in the OpenJpeg case - return false; + llassert(user_data); +#if WANT_VERBOSE_OPJ_SPAM + LL_WARNS("OpenJPEG") << msg << LL_ENDL; +#endif } -bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) +static OPJ_SIZE_T opj_read(void * buffer, OPJ_SIZE_T bytes, void* user_data) { - // - // FIXME: Get the comment field out of the texture - // - - LLTimer decode_timer; - - opj_dparameters_t parameters; /* decompression parameters */ - opj_event_mgr_t event_mgr; /* event manager */ - opj_image_t *image = NULL; - - opj_dinfo_t* dinfo = NULL; /* handle to a decompressor */ - opj_cio_t *cio = NULL; - - - /* configure the event callbacks (not required) */ - memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); - event_mgr.error_handler = error_callback; - event_mgr.warning_handler = warning_callback; - event_mgr.info_handler = info_callback; - - /* set decoding parameters to default values */ - opj_set_default_decoder_parameters(¶meters); - - parameters.cp_reduce = base.getRawDiscardLevel(); - - /* decode the code-stream */ - /* ---------------------- */ - - /* JPEG-2000 codestream */ - - /* get a decoder handle */ - dinfo = opj_create_decompress(CODEC_J2K); - - /* catch events using our callbacks and give a local context */ - opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr); - - /* setup the decoder decoding parameters using user parameters */ - opj_setup_decoder(dinfo, ¶meters); - - /* open a byte stream */ - cio = opj_cio_open((opj_common_ptr)dinfo, base.getData(), base.getDataSize()); - - /* decode the stream and fill the image structure */ - image = opj_decode(dinfo, cio); - - /* close the byte stream */ - opj_cio_close(cio); - - /* free remaining structures */ - if(dinfo) - { - opj_destroy_decompress(dinfo); - } - - // The image decode failed if the return was NULL or the component - // count was zero. The latter is just a sanity check before we - // dereference the array. - if(!image || !image->numcomps) - { - LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image!" << LL_ENDL; - if (image) - { - opj_image_destroy(image); - } - - return true; // done - } - - // sometimes we get bad data out of the cache - check to see if the decode succeeded - for (S32 i = 0; i < image->numcomps; i++) - { - if (image->comps[i].factor != base.getRawDiscardLevel()) - { - // if we didn't get the discard level we're expecting, fail - opj_image_destroy(image); - base.mDecoding = false; - return true; - } - } - - if(image->numcomps <= first_channel) - { - LL_WARNS() << "trying to decode more channels than are present in image: numcomps: " << image->numcomps << " first_channel: " << first_channel << LL_ENDL; - if (image) - { - opj_image_destroy(image); - } - - return true; - } - - // Copy image data into our raw image format (instead of the separate channel format - - S32 img_components = image->numcomps; - S32 channels = img_components - first_channel; - if( channels > max_channel_count ) - channels = max_channel_count; - - // Component buffers are allocated in an image width by height buffer. - // The image placed in that buffer is ceil(width/2^factor) by - // ceil(height/2^factor) and if the factor isn't zero it will be at the - // top left of the buffer with black filled in the rest of the pixels. - // It is integer math so the formula is written in ceildivpo2. - // (Assuming all the components have the same width, height and - // factor.) - S32 comp_width = image->comps[0].w; - S32 f=image->comps[0].factor; - S32 width = ceildivpow2(image->x1 - image->x0, f); - S32 height = ceildivpow2(image->y1 - image->y0, f); - raw_image.resize(width, height, channels); - U8 *rawp = raw_image.getData(); - - // first_channel is what channel to start copying from - // dest is what channel to copy to. first_channel comes from the - // argument, dest always starts writing at channel zero. - for (S32 comp = first_channel, dest=0; comp < first_channel + channels; - comp++, dest++) - { - if (image->comps[comp].data) - { - S32 offset = dest; - for (S32 y = (height - 1); y >= 0; y--) - { - for (S32 x = 0; x < width; x++) - { - rawp[offset] = image->comps[comp].data[y*comp_width + x]; - offset += channels; - } - } - } - else // Some rare OpenJPEG versions have this bug. - { - LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image! (NULL comp data - OpenJPEG bug)" << LL_ENDL; - opj_image_destroy(image); - - return true; // done - } - } - - /* free image data structure */ - opj_image_destroy(image); - - return true; // done + llassert(user_data); + JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); + OPJ_SIZE_T remainder = (jpeg_codec->size - jpeg_codec->offset); + if (remainder <= 0) + { + jpeg_codec->offset = jpeg_codec->size; + // Indicate end of stream (hacky?) + return (OPJ_OFF_T)-1; + } + OPJ_SIZE_T to_read = llclamp(U32(bytes), U32(0), U32(remainder)); + memcpy(buffer, jpeg_codec->buffer + jpeg_codec->offset, to_read); + jpeg_codec->offset += to_read; + return to_read; } - -bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible) +static OPJ_SIZE_T opj_write(void * buffer, OPJ_SIZE_T bytes, void* user_data) { - const S32 MAX_COMPS = 5; - opj_cparameters_t parameters; /* compression parameters */ - opj_event_mgr_t event_mgr; /* event manager */ - - - /* - configure the event callbacks (not required) - setting of each callback is optional - */ - memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); - event_mgr.error_handler = error_callback; - event_mgr.warning_handler = warning_callback; - event_mgr.info_handler = info_callback; - - /* set encoding parameters to default values */ - opj_set_default_encoder_parameters(¶meters); - parameters.cod_format = 0; - parameters.cp_disto_alloc = 1; - - if (reversible) - { - parameters.tcp_numlayers = 1; - parameters.tcp_rates[0] = 0.0f; - } - else - { - parameters.tcp_numlayers = 5; - parameters.tcp_rates[0] = 1920.0f; - parameters.tcp_rates[1] = 480.0f; - parameters.tcp_rates[2] = 120.0f; - parameters.tcp_rates[3] = 30.0f; - parameters.tcp_rates[4] = 10.0f; - parameters.irreversible = 1; - if (raw_image.getComponents() >= 3) - { - parameters.tcp_mct = 1; - } - } - - if (!comment_text) - { - parameters.cp_comment = (char *) ""; - } - else - { - // Awful hacky cast, too lazy to copy right now. - parameters.cp_comment = (char *) comment_text; - } - - // - // Fill in the source image from our raw image - // - OPJ_COLOR_SPACE color_space = CLRSPC_SRGB; - opj_image_cmptparm_t cmptparm[MAX_COMPS]; - opj_image_t * image = NULL; - S32 numcomps = raw_image.getComponents(); - S32 width = raw_image.getWidth(); - S32 height = raw_image.getHeight(); - - memset(&cmptparm[0], 0, MAX_COMPS * sizeof(opj_image_cmptparm_t)); - for(S32 c = 0; c < numcomps; c++) { - cmptparm[c].prec = 8; - cmptparm[c].bpp = 8; - cmptparm[c].sgnd = 0; - cmptparm[c].dx = parameters.subsampling_dx; - cmptparm[c].dy = parameters.subsampling_dy; - cmptparm[c].w = width; - cmptparm[c].h = height; - } - - /* create the image */ - image = opj_image_create(numcomps, &cmptparm[0], color_space); - - image->x1 = width; - image->y1 = height; - - S32 i = 0; - const U8 *src_datap = raw_image.getData(); - for (S32 y = height - 1; y >= 0; y--) - { - for (S32 x = 0; x < width; x++) - { - const U8 *pixel = src_datap + (y*width + x) * numcomps; - for (S32 c = 0; c < numcomps; c++) - { - image->comps[c].data[i] = *pixel; - pixel++; - } - i++; - } - } - - - - /* encode the destination image */ - /* ---------------------------- */ - - int codestream_length; - opj_cio_t *cio = NULL; - - /* get a J2K compressor handle */ - opj_cinfo_t* cinfo = opj_create_compress(CODEC_J2K); - - /* catch events using our callbacks and give a local context */ - opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, stderr); - - /* setup the encoder parameters using the current image and using user parameters */ - opj_setup_encoder(cinfo, ¶meters, image); - - /* open a byte stream for writing */ - /* allocate memory for all tiles */ - cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0); - - /* encode the image */ - bool bSuccess = opj_encode(cinfo, cio, image, NULL); - if (!bSuccess) - { - opj_cio_close(cio); - LL_DEBUGS("Texture") << "Failed to encode image." << LL_ENDL; - return false; - } - codestream_length = cio_tell(cio); - - base.copyData(cio->buffer, codestream_length); - base.updateData(); // set width, height - - /* close and free the byte stream */ - opj_cio_close(cio); - - /* free remaining compression structures */ - opj_destroy_compress(cinfo); - - - /* free user parameters structure */ - if(parameters.cp_matrice) free(parameters.cp_matrice); - - /* free image data */ - opj_image_destroy(image); - return true; + llassert(user_data); + JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); + OPJ_SIZE_T remainder = jpeg_codec->size - jpeg_codec->offset; + if (remainder < bytes) + { + OPJ_SIZE_T new_size = jpeg_codec->size + (bytes - remainder); + U8* new_buffer = (U8*)ll_aligned_malloc_16(new_size); + memcpy(new_buffer, jpeg_codec->buffer, jpeg_codec->offset); + U8* old_buffer = jpeg_codec->buffer; + jpeg_codec->buffer = new_buffer; + ll_aligned_free_16(old_buffer); + jpeg_codec->size = new_size; + } + memcpy(jpeg_codec->buffer + jpeg_codec->offset, buffer, bytes); + jpeg_codec->offset += bytes; + return bytes; } -bool LLImageJ2COJ::getMetadata(LLImageJ2C &base) +static OPJ_OFF_T opj_skip(OPJ_OFF_T bytes, void* user_data) { - // - // FIXME: We get metadata by decoding the ENTIRE image. - // - - // Update the raw discard level - base.updateRawDiscardLevel(); - - opj_dparameters_t parameters; /* decompression parameters */ - opj_event_mgr_t event_mgr; /* event manager */ - opj_image_t *image = NULL; - - opj_dinfo_t* dinfo = NULL; /* handle to a decompressor */ - opj_cio_t *cio = NULL; - - - /* configure the event callbacks (not required) */ - memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); - event_mgr.error_handler = error_callback; - event_mgr.warning_handler = warning_callback; - event_mgr.info_handler = info_callback; - - /* set decoding parameters to default values */ - opj_set_default_decoder_parameters(¶meters); - - // Only decode what's required to get the size data. - parameters.cp_limit_decoding=LIMIT_TO_MAIN_HEADER; + JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); + jpeg_codec->offset += bytes; + + if (jpeg_codec->offset > jpeg_codec->size) + { + jpeg_codec->offset = jpeg_codec->size; + // Indicate end of stream + return (OPJ_OFF_T)-1; + } + + if (jpeg_codec->offset < 0) + { + // Shouldn't be possible? + jpeg_codec->offset = 0; + return (OPJ_OFF_T)-1; + } + + return bytes; +} - //parameters.cp_reduce = mRawDiscardLevel; +static OPJ_BOOL opj_seek(OPJ_OFF_T bytes, void * user_data) +{ + JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); + jpeg_codec->offset = bytes; + jpeg_codec->offset = llclamp(U32(jpeg_codec->offset), U32(0), U32(jpeg_codec->size)); + return OPJ_TRUE; +} - /* decode the code-stream */ - /* ---------------------- */ +static void opj_free_user_data(void * user_data) +{ + JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); + // Don't free, data is managed externally + jpeg_codec->buffer = nullptr; + jpeg_codec->size = 0; + jpeg_codec->offset = 0; +} - /* JPEG-2000 codestream */ +static void opj_free_user_data_write(void * user_data) +{ + JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data); + // Free, data was allocated here + ll_aligned_free_16(jpeg_codec->buffer); + jpeg_codec->buffer = nullptr; + jpeg_codec->size = 0; + jpeg_codec->offset = 0; +} - /* get a decoder handle */ - dinfo = opj_create_decompress(CODEC_J2K); +class JPEG2KDecode : public JPEG2KBase +{ +public: + + JPEG2KDecode(S8 discardLevel) + { + memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); + memset(¶meters, 0, sizeof(opj_dparameters_t)); + event_mgr.error_handler = error_callback; + event_mgr.warning_handler = warning_callback; + event_mgr.info_handler = info_callback; + opj_set_default_decoder_parameters(¶meters); + parameters.cp_reduce = discardLevel; + } + + ~JPEG2KDecode() + { + if (decoder) + { + opj_destroy_codec(decoder); + } + decoder = nullptr; + + if (image) + { + opj_image_destroy(image); + } + image = nullptr; + + if (stream) + { + opj_stream_destroy(stream); + } + stream = nullptr; + + if (codestream_info) + { + opj_destroy_cstr_info(&codestream_info); + } + codestream_info = nullptr; + } + + bool readHeader( + U8* data, + U32 dataSize, + S32& widthOut, + S32& heightOut, + S32& components, + S32& discard_level) + { + parameters.flags |= OPJ_DPARAMETERS_DUMP_FLAG; + + decoder = opj_create_decompress(OPJ_CODEC_J2K); + + if (!opj_setup_decoder(decoder, ¶meters)) + { + return false; + } + + if (stream) + { + opj_stream_destroy(stream); + } + + stream = opj_stream_create(dataSize, true); + if (!stream) + { + return false; + } + + opj_stream_set_user_data(stream, this, opj_free_user_data); + opj_stream_set_user_data_length(stream, dataSize); + opj_stream_set_read_function(stream, opj_read); + opj_stream_set_write_function(stream, opj_write); + opj_stream_set_skip_function(stream, opj_skip); + opj_stream_set_seek_function(stream, opj_seek); + + buffer = data; + size = dataSize; + offset = 0; + + // enable decoding partially loaded images + opj_decoder_set_strict_mode(decoder, OPJ_FALSE); + + /* Read the main header of the codestream and if necessary the JP2 boxes*/ + if (!opj_read_header((opj_stream_t*)stream, decoder, &image)) + { + return false; + } + + codestream_info = opj_get_cstr_info(decoder); + + if (!codestream_info) + { + return false; + } + + U32 tileDimX = codestream_info->tdx; + U32 tileDimY = codestream_info->tdy; + U32 tilesW = codestream_info->tw; + U32 tilesH = codestream_info->th; + + widthOut = S32(tilesW * tileDimX); + heightOut = S32(tilesH * tileDimY); + components = codestream_info->nbcomps; + + discard_level = 0; + while (tilesW > 1 && tilesH > 1 && discard_level < MAX_DISCARD_LEVEL) + { + discard_level++; + tilesW >>= 1; + tilesH >>= 1; + } + + return true; + } + + bool decode(U8* data, U32 dataSize, U32* channels, U8 discard_level) + { + parameters.flags &= ~OPJ_DPARAMETERS_DUMP_FLAG; + + decoder = opj_create_decompress(OPJ_CODEC_J2K); + opj_setup_decoder(decoder, ¶meters); + + opj_set_info_handler(decoder, opj_info, this); + opj_set_warning_handler(decoder, opj_warn, this); + opj_set_error_handler(decoder, opj_error, this); + + if (stream) + { + opj_stream_destroy(stream); + } + + stream = opj_stream_create(dataSize, true); + if (!stream) + { + return false; + } + + opj_stream_set_user_data(stream, this, opj_free_user_data); + opj_stream_set_user_data_length(stream, dataSize); + opj_stream_set_read_function(stream, opj_read); + opj_stream_set_write_function(stream, opj_write); + opj_stream_set_skip_function(stream, opj_skip); + opj_stream_set_seek_function(stream, opj_seek); + + buffer = data; + size = dataSize; + offset = 0; + + if (image) + { + opj_image_destroy(image); + image = nullptr; + } + + // needs to happen before opj_read_header and opj_decode... + opj_set_decoded_resolution_factor(decoder, discard_level); + + // enable decoding partially loaded images + opj_decoder_set_strict_mode(decoder, OPJ_FALSE); + + if (!opj_read_header(stream, decoder, &image)) + { + return false; + } + + // needs to happen before decode which may fail + if (channels) + { + *channels = image->numcomps; + } + + OPJ_BOOL decoded = opj_decode(decoder, stream, image); + + // count was zero. The latter is just a sanity check before we + // dereference the array. + if (!decoded || !image || !image->numcomps) + { + opj_end_decompress(decoder, stream); + return false; + } + + opj_end_decompress(decoder, stream); + + return true; + } + + opj_image_t* getImage() { return image; } + +private: + opj_dparameters_t parameters; + opj_event_mgr_t event_mgr; + opj_image_t* image = nullptr; + opj_codec_t* decoder = nullptr; + opj_stream_t* stream = nullptr; + opj_codestream_info_v2_t* codestream_info = nullptr; +}; + +class JPEG2KEncode : public JPEG2KBase +{ +public: + const OPJ_UINT32 TILE_SIZE = 64 * 64 * 3; + + JPEG2KEncode(const char* comment_text_in, bool reversible) + { + memset(¶meters, 0, sizeof(opj_cparameters_t)); + memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); + event_mgr.error_handler = error_callback; + event_mgr.warning_handler = warning_callback; + event_mgr.info_handler = info_callback; + + opj_set_default_encoder_parameters(¶meters); + parameters.cod_format = OPJ_CODEC_J2K; + parameters.cp_disto_alloc = 1; + parameters.max_cs_size = (1 << 15); + + if (reversible) + { + parameters.tcp_numlayers = 1; + parameters.tcp_rates[0] = 1.0f; + } + else + { + parameters.tcp_numlayers = 5; + parameters.tcp_rates[0] = 1920.0f; + parameters.tcp_rates[1] = 960.0f; + parameters.tcp_rates[2] = 480.0f; + parameters.tcp_rates[3] = 120.0f; + parameters.tcp_rates[4] = 30.0f; + parameters.irreversible = 1; + parameters.tcp_mct = 1; + } + + if (comment_text) + { + free(comment_text); + } + comment_text = comment_text_in ? strdup(comment_text_in) : nullptr; + + parameters.cp_comment = comment_text ? comment_text : (char*)"no comment"; + llassert(parameters.cp_comment); + } + + ~JPEG2KEncode() + { + if (encoder) + { + opj_destroy_codec(encoder); + } + encoder = nullptr; + + if (image) + { + opj_image_destroy(image); + } + image = nullptr; + + if (stream) + { + opj_stream_destroy(stream); + } + stream = nullptr; + + if (comment_text) + { + free(comment_text); + } + comment_text = nullptr; + } + + bool encode(const LLImageRaw& rawImageIn, LLImageJ2C &compressedImageOut) + { + setImage(rawImageIn); + + encoder = opj_create_compress(OPJ_CODEC_J2K); + + parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0; + parameters.cod_format = OPJ_CODEC_J2K; + parameters.prog_order = OPJ_RLCP; + parameters.cp_disto_alloc = 1; + + if (!opj_setup_encoder(encoder, ¶meters, image)) + { + return false; + } + + opj_set_info_handler(encoder, opj_info, this); + opj_set_warning_handler(encoder, opj_warn, this); + opj_set_error_handler(encoder, opj_error, this); + + U32 tile_count = (rawImageIn.getWidth() >> 6) * (rawImageIn.getHeight() >> 6); + U32 data_size_guess = tile_count * TILE_SIZE; + + // will be freed in opj_free_user_data_write + buffer = (U8*)ll_aligned_malloc_16(data_size_guess); + size = data_size_guess; + offset = 0; + + memset(buffer, 0, data_size_guess); + + if (stream) + { + opj_stream_destroy(stream); + } + + stream = opj_stream_create(data_size_guess, false); + if (!stream) + { + return false; + } + + opj_stream_set_user_data(stream, this, opj_free_user_data_write); + opj_stream_set_user_data_length(stream, data_size_guess); + opj_stream_set_read_function(stream, opj_read); + opj_stream_set_write_function(stream, opj_write); + opj_stream_set_skip_function(stream, opj_skip); + opj_stream_set_seek_function(stream, opj_seek); + + OPJ_BOOL started = opj_start_compress(encoder, image, stream); + + if (!started) + { + return false; + } + + if (!opj_encode(encoder, stream)) + { + return false; + } + + OPJ_BOOL encoded = opj_end_compress(encoder, stream); + + // if we successfully encoded, then stream out the compressed data... + if (encoded) + { + // "append" (set) the data we "streamed" (memcopied) for writing to the formatted image + // with side-effect of setting the actually encoded size to same + compressedImageOut.allocateData(offset); + memcpy(compressedImageOut.getData(), buffer, offset); + compressedImageOut.updateData(); // update width, height etc from header + } + return encoded; + } + + void setImage(const LLImageRaw& raw) + { + opj_image_cmptparm_t cmptparm[MAX_ENCODED_DISCARD_LEVELS]; + memset(&cmptparm[0], 0, MAX_ENCODED_DISCARD_LEVELS * sizeof(opj_image_cmptparm_t)); + + S32 numcomps = raw.getComponents(); + S32 width = raw.getWidth(); + S32 height = raw.getHeight(); + + for (S32 c = 0; c < numcomps; c++) + { + cmptparm[c].prec = 8; + cmptparm[c].bpp = 8; + cmptparm[c].sgnd = 0; + cmptparm[c].dx = parameters.subsampling_dx; + cmptparm[c].dy = parameters.subsampling_dy; + cmptparm[c].w = width; + cmptparm[c].h = height; + } + + image = opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB); + + image->x1 = width; + image->y1 = height; + + const U8 *src_datap = raw.getData(); + + S32 i = 0; + for (S32 y = height - 1; y >= 0; y--) + { + for (S32 x = 0; x < width; x++) + { + const U8 *pixel = src_datap + (y*width + x) * numcomps; + for (S32 c = 0; c < numcomps; c++) + { + image->comps[c].data[i] = *pixel; + pixel++; + } + i++; + } + } + + // This likely works, but there seems to be an issue openjpeg side + // check over after gixing that. + + // De-interleave to component plane data + /* + switch (numcomps) + { + case 0: + default: + break; + + case 1: + { + U32 rBitDepth = image->comps[0].bpp; + U32 bytesPerPixel = rBitDepth >> 3; + memcpy(image->comps[0].data, src, width * height * bytesPerPixel); + } + break; + + case 2: + { + U32 rBitDepth = image->comps[0].bpp; + U32 gBitDepth = image->comps[1].bpp; + U32 totalBitDepth = rBitDepth + gBitDepth; + U32 bytesPerPixel = totalBitDepth >> 3; + U32 stride = width * bytesPerPixel; + U32 offset = 0; + for (S32 y = height - 1; y >= 0; y--) + { + const U8* component = src + (y * stride); + for (S32 x = 0; x < width; x++) + { + image->comps[0].data[offset] = *component++; + image->comps[1].data[offset] = *component++; + offset++; + } + } + } + break; + + case 3: + { + U32 rBitDepth = image->comps[0].bpp; + U32 gBitDepth = image->comps[1].bpp; + U32 bBitDepth = image->comps[2].bpp; + U32 totalBitDepth = rBitDepth + gBitDepth + bBitDepth; + U32 bytesPerPixel = totalBitDepth >> 3; + U32 stride = width * bytesPerPixel; + U32 offset = 0; + for (S32 y = height - 1; y >= 0; y--) + { + const U8* component = src + (y * stride); + for (S32 x = 0; x < width; x++) + { + image->comps[0].data[offset] = *component++; + image->comps[1].data[offset] = *component++; + image->comps[2].data[offset] = *component++; + offset++; + } + } + } + break; + + + case 4: + { + U32 rBitDepth = image->comps[0].bpp; + U32 gBitDepth = image->comps[1].bpp; + U32 bBitDepth = image->comps[2].bpp; + U32 aBitDepth = image->comps[3].bpp; + + U32 totalBitDepth = rBitDepth + gBitDepth + bBitDepth + aBitDepth; + U32 bytesPerPixel = totalBitDepth >> 3; + + U32 stride = width * bytesPerPixel; + U32 offset = 0; + for (S32 y = height - 1; y >= 0; y--) + { + const U8* component = src + (y * stride); + for (S32 x = 0; x < width; x++) + { + image->comps[0].data[offset] = *component++; + image->comps[1].data[offset] = *component++; + image->comps[2].data[offset] = *component++; + image->comps[3].data[offset] = *component++; + offset++; + } + } + } + break; + }*/ + } + + opj_image_t* getImage() { return image; } + +private: + opj_cparameters_t parameters; + opj_event_mgr_t event_mgr; + opj_image_t* image = nullptr; + opj_codec_t* encoder = nullptr; + opj_stream_t* stream = nullptr; + char* comment_text = nullptr; +}; - /* catch events using our callbacks and give a local context */ - opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr); - /* setup the decoder decoding parameters using user parameters */ - opj_setup_decoder(dinfo, ¶meters); +LLImageJ2COJ::LLImageJ2COJ() + : LLImageJ2CImpl() +{ +} - /* open a byte stream */ - cio = opj_cio_open((opj_common_ptr)dinfo, base.getData(), base.getDataSize()); - /* decode the stream and fill the image structure */ - image = opj_decode(dinfo, cio); +LLImageJ2COJ::~LLImageJ2COJ() +{ +} - /* close the byte stream */ - opj_cio_close(cio); +bool LLImageJ2COJ::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region) +{ + base.mDiscardLevel = discard_level; + return false; +} - /* free remaining structures */ - if(dinfo) - { - opj_destroy_decompress(dinfo); - } +bool LLImageJ2COJ::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels) +{ + // No specific implementation for this method in the OpenJpeg case + return false; +} - if(!image) - { - LL_WARNS() << "ERROR -> getMetadata: failed to decode image!" << LL_ENDL; - return false; - } +bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) +{ + JPEG2KDecode decoder(0); + + U32 image_channels = 0; + S32 data_size = base.getDataSize(); + S32 max_bytes = (base.getMaxBytes() ? base.getMaxBytes() : data_size); + bool decoded = decoder.decode(base.getData(), max_bytes, &image_channels, base.mDiscardLevel); + + // set correct channel count early so failed decodes don't miss it... + S32 channels = (S32)image_channels - first_channel; + channels = llmin(channels, max_channel_count); + + if (!decoded) + { + // reset the channel count if necessary + if (raw_image.getComponents() != channels) + { + raw_image.resize(raw_image.getWidth(), raw_image.getHeight(), S8(channels)); + } + + LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image!" << LL_ENDL; + return true; // done + } + + opj_image_t *image = decoder.getImage(); + + // Component buffers are allocated in an image width by height buffer. + // The image placed in that buffer is ceil(width/2^factor) by + // ceil(height/2^factor) and if the factor isn't zero it will be at the + // top left of the buffer with black filled in the rest of the pixels. + // It is integer math so the formula is written in ceildivpo2. + // (Assuming all the components have the same width, height and + // factor.) + U32 comp_width = image->comps[0].w; // leave this unshifted by 'f' discard factor, the strides are always for the full buffer width + U32 f = image->comps[0].factor; + + // do size the texture to the mem we'll acrually use... + U32 width = image->comps[0].w; + U32 height = image->comps[0].h; + + raw_image.resize(U16(width), U16(height), S8(channels)); + + U8 *rawp = raw_image.getData(); + + // first_channel is what channel to start copying from + // dest is what channel to copy to. first_channel comes from the + // argument, dest always starts writing at channel zero. + for (S32 comp = first_channel, dest = 0; comp < first_channel + channels; comp++, dest++) + { + llassert(image->comps[comp].data); + if (image->comps[comp].data) + { + S32 offset = dest; + for (S32 y = (height - 1); y >= 0; y--) + { + for (S32 x = 0; x < width; x++) + { + rawp[offset] = image->comps[comp].data[y*comp_width + x]; + offset += channels; + } + } + } + else // Some rare OpenJPEG versions have this bug. + { + LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed! (OpenJPEG bug)" << LL_ENDL; + } + } + + base.setDiscardLevel(f); + + return true; // done +} - // Copy image data into our raw image format (instead of the separate channel format - S32 width = 0; - S32 height = 0; - S32 img_components = image->numcomps; - width = image->x1 - image->x0; - height = image->y1 - image->y0; - base.setSize(width, height, img_components); +bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible) +{ + JPEG2KEncode encode(comment_text, reversible); + bool encoded = encode.encode(raw_image, base); + if (encoded) + { + LL_WARNS() << "Openjpeg encoding implementation isn't complete, returning false" << LL_ENDL; + } + return encoded; + //return false; +} - /* free image data structure */ - opj_image_destroy(image); - return true; +bool LLImageJ2COJ::getMetadata(LLImageJ2C &base) +{ + JPEG2KDecode decode(0); + + S32 width = 0; + S32 height = 0; + S32 components = 0; + S32 discard_level = 0; + + U32 dataSize = base.getDataSize(); + U8* data = base.getData(); + bool header_read = decode.readHeader(data, dataSize, width, height, components, discard_level); + if (!header_read) + { + return false; + } + + base.mDiscardLevel = discard_level; + base.setSize(width, height, components); + return true; } diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 93f1d508f3..91457fbebe 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2391,7 +2391,25 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL; return false; } - + return unpackVolumeFacesInternal(mdl); +} + +bool LLVolume::unpackVolumeFaces(U8* in_data, S32 size) +{ + //input data is now pointing at a zlib compressed block of LLSD + //decompress block + LLSD mdl; + U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, in_data, size); + if (uzip_result != LLUZipHelper::ZR_OK) + { + LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL; + return false; + } + return unpackVolumeFacesInternal(mdl); +} + +bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) +{ { U32 face_count = mdl.size(); diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 9697952f5b..b53b0fd1cc 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -1096,8 +1096,12 @@ protected: BOOL generate(); void createVolumeFaces(); public: - virtual bool unpackVolumeFaces(std::istream& is, S32 size); + bool unpackVolumeFaces(std::istream& is, S32 size); + bool unpackVolumeFaces(U8* in_data, S32 size); +private: + bool unpackVolumeFacesInternal(const LLSD& mdl); +public: virtual void setMeshAssetLoaded(BOOL loaded); virtual BOOL isMeshAssetLoaded(); diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 555164f3b0..444d9a5366 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -1389,6 +1389,16 @@ LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin): fromLLSD(skin); } +LLMeshSkinInfo::LLMeshSkinInfo(const LLUUID& mesh_id, LLSD& skin) : + mMeshID(mesh_id), + mPelvisOffset(0.0), + mLockScaleIfJointPosition(false), + mInvalidJointsScrubbed(false), + mJointNumsInitialized(false) +{ + fromLLSD(skin); +} + void LLMeshSkinInfo::fromLLSD(LLSD& skin) { if (skin.has("joint_names")) diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index a6ab96ab18..9c99bb75a0 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -41,12 +41,13 @@ class domMesh; #define MAX_MODEL_FACES 8 LL_ALIGN_PREFIX(16) -class LLMeshSkinInfo +class LLMeshSkinInfo : public LLRefCount { LL_ALIGN_NEW public: LLMeshSkinInfo(); LLMeshSkinInfo(LLSD& data); + LLMeshSkinInfo(const LLUUID& mesh_id, LLSD& data); void fromLLSD(LLSD& data); LLSD asLLSD(bool include_joints, bool lock_scale_if_joint_position) const; void updateHash(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7d78ec9e3c..4fe83f0c45 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1825,9 +1825,9 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/Release/libcollada14dom22.dll ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libcollada14dom22.dll ${SHARED_LIB_STAGING_DIR}/Debug/libcollada14dom22-d.dll - ${SHARED_LIB_STAGING_DIR}/Release/openjpeg.dll - ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjpeg.dll - ${SHARED_LIB_STAGING_DIR}/Debug/openjpegd.dll + ${SHARED_LIB_STAGING_DIR}/Release/openjp2.dll + ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjp2.dll + ${SHARED_LIB_STAGING_DIR}/Debug/openjp2.dll ${SHARED_LIB_STAGING_DIR}/Release/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/Debug/libhunspell.dll diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl index 331249dc33..de22312d3c 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl @@ -25,6 +25,17 @@ /*[EXTRA_CODE_HERE]*/ +// Inputs +VARYING vec4 vary_HazeColor; +VARYING float vary_LightNormPosDot; + +uniform sampler2D rainbow_map; +uniform sampler2D halo_map; + +uniform float moisture_level; +uniform float droplet_radius; +uniform float ice_level; + #ifdef DEFINE_GL_FRAGCOLOR out vec4 frag_data[3]; #else @@ -35,11 +46,34 @@ out vec4 frag_data[3]; // The fragment shader for the sky ///////////////////////////////////////////////////////////////////////// -VARYING vec4 vary_HazeColor; +vec3 rainbow(float d) +{ + // 'Interesting' values of d are -0.75 .. -0.825, i.e. when view vec nearly opposite of sun vec + // Rainbox tex is mapped with REPEAT, so -.75 as tex coord is same as 0.25. -0.825 -> 0.175. etc. + // SL-13629 + // Unfortunately the texture is inverted, so we need to invert the y coord, but keep the 'interesting' + // part within the same 0.175..0.250 range, i.e. d = (1 - d) - 1.575 + d = clamp(-0.575 - d, 0.0, 1.0); + + // With the colors in the lower 1/4 of the texture, inverting the coords leaves most of it inaccessible. + // So, we can stretch the texcoord above the colors (ie > 0.25) to fill the entire remaining coordinate + // space. This improves gradation, reduces banding within the rainbow interior. (1-0.25) / (0.425/0.25) = 4.2857 + float interior_coord = max(0.0, d - 0.25) * 4.2857; + d = clamp(d, 0.0, 0.25) + interior_coord; + + float rad = (droplet_radius - 5.0f) / 1024.0f; + return pow(texture2D(rainbow_map, vec2(rad+0.5, d)).rgb, vec3(1.8)) * moisture_level; +} + +vec3 halo22(float d) +{ + d = clamp(d, 0.1, 1.0); + float v = sqrt(clamp(1 - (d * d), 0, 1)); + return texture2D(halo_map, vec2(0, v)).rgb * ice_level; +} /// Soft clips the light with a gamma correction vec3 scaleSoftClip(vec3 light); -vec3 srgb_to_linear(vec3 c); void main() { @@ -48,14 +82,18 @@ void main() // the fragment) if the sky wouldn't show up because the clouds // are fully opaque. - vec4 color; - color = vary_HazeColor; + vec4 color = vary_HazeColor; + float rel_pos_lightnorm = vary_LightNormPosDot; + float optic_d = rel_pos_lightnorm; + vec3 halo_22 = halo22(optic_d); + color.rgb += rainbow(optic_d); + color.rgb += halo_22; color.rgb *= 2.; color.rgb = scaleSoftClip(color.rgb); - /// Gamma correct for WL (soft clip effect). - frag_data[0] = vec4(color.rgb, 0.0); + // Gamma correct for WL (soft clip effect). + frag_data[0] = vec4(color.rgb, 1.0); frag_data[1] = vec4(0.0,0.0,0.0,0.0); frag_data[2] = vec4(0.0,0.0,0.0,1.0); //1.0 in norm.w masks off fog diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl index 28a1faf24f..6db4690bff 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl @@ -33,6 +33,7 @@ ATTRIBUTE vec3 position; // Output parameters VARYING vec4 vary_HazeColor; +VARYING float vary_LightNormPosDot; // Inputs uniform vec3 camPosLocal; @@ -72,27 +73,29 @@ void main() vec3 rel_pos = position.xyz - camPosLocal.xyz + vec3(0, 50, 0); // Adj position vector to clamp altitude - if (rel_pos.y > 0) + if (rel_pos.y > 0.) { rel_pos *= (max_y / rel_pos.y); } - if (rel_pos.y < 0) + if (rel_pos.y < 0.) { rel_pos *= (-32000. / rel_pos.y); } - // Can normalize then - vec3 rel_pos_norm = normalize(rel_pos); + // Normalized + vec3 rel_pos_norm = normalize(rel_pos); + float rel_pos_len = length(rel_pos); - float rel_pos_len = length(rel_pos); + // Grab this value and pass to frag shader for rainbows + float rel_pos_lightnorm_dot = dot(rel_pos_norm, lightnorm.xyz); + vary_LightNormPosDot = rel_pos_lightnorm_dot; // Initialize temp variables vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; - vec4 light_atten; // Sunlight attenuation effect (hue and brightness) due to atmosphere // this is used later for sunlight modulation at various altitudes - light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y); + vec4 light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y); // Calculate relative weights vec4 combined_haze = abs(blue_density) + vec4(abs(haze_density)); @@ -112,7 +115,7 @@ void main() combined_haze = exp(-combined_haze * density_dist); // Compute haze glow - float haze_glow = 1.0 - dot(rel_pos_norm, lightnorm.xyz); + float haze_glow = 1.0 - rel_pos_lightnorm_dot; // haze_glow is 0 at the sun and increases away from sun haze_glow = max(haze_glow, .001); // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) @@ -123,30 +126,30 @@ void main() // Add "minimum anti-solar illumination" // For sun, add to glow. For moon, remove glow entirely. SL-13768 - haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (haze_glow + 0.25); + haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (sun_moon_glow_factor * (haze_glow + 0.25)); - vec4 color = - (blue_horizon * blue_weight * (sunlight + ambient_color) + (haze_horizon * haze_weight) * (sunlight * haze_glow + ambient_color)); + // Haze color above cloud + vec4 color = (blue_horizon * blue_weight * (sunlight + ambient_color) + + (haze_horizon * haze_weight) * (sunlight * haze_glow + ambient_color)); // Final atmosphere additive color *= (1. - combined_haze); // Increase ambient when there are more clouds - vec4 tmpAmbient = ambient_color; - tmpAmbient += max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5; + vec4 ambient = ambient_color + max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5; // Dim sunlight by cloud shadow percentage sunlight *= max(0.0, (1. - cloud_shadow)); // Haze color below cloud - vec4 additiveColorBelowCloud = - (blue_horizon * blue_weight * (sunlight + tmpAmbient) + (haze_horizon * haze_weight) * (sunlight * haze_glow + tmpAmbient)); + vec4 add_below_cloud = (blue_horizon * blue_weight * (sunlight + ambient) + + (haze_horizon * haze_weight) * (sunlight * haze_glow + ambient)); // Attenuate cloud color by atmosphere combined_haze = sqrt(combined_haze); // less atmos opacity (more transparency) below clouds // At horizon, blend high altitude sky color towards the darker color below the clouds - color += (additiveColorBelowCloud - color) * (1. - sqrt(combined_haze)); + color += (add_below_cloud - color) * (1. - sqrt(combined_haze)); // Haze color above cloud vary_HazeColor = color; diff --git a/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl deleted file mode 100644 index 6841a8194f..0000000000 --- a/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl +++ /dev/null @@ -1,199 +0,0 @@ -/** - * @file class2/deferred/skyF.glsl - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2005, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -uniform mat4 modelview_projection_matrix; - -// SKY //////////////////////////////////////////////////////////////////////// -// The vertex shader for creating the atmospheric sky -/////////////////////////////////////////////////////////////////////////////// - -// Inputs -uniform vec3 camPosLocal; - -uniform vec4 lightnorm; -uniform vec4 sunlight_color; -uniform vec4 moonlight_color; -uniform int sun_up_factor; -uniform vec4 ambient_color; -uniform vec4 blue_horizon; -uniform vec4 blue_density; -uniform float haze_horizon; -uniform float haze_density; - -uniform float cloud_shadow; -uniform float density_multiplier; -uniform float distance_multiplier; -uniform float max_y; - -uniform vec4 glow; -uniform float sun_moon_glow_factor; - -uniform vec4 cloud_color; - -#ifdef DEFINE_GL_FRAGCOLOR -out vec4 frag_data[3]; -#else -#define frag_data gl_FragData -#endif - -VARYING vec3 pos; - -///////////////////////////////////////////////////////////////////////// -// The fragment shader for the sky -///////////////////////////////////////////////////////////////////////// - -uniform sampler2D rainbow_map; -uniform sampler2D halo_map; - -uniform float moisture_level; -uniform float droplet_radius; -uniform float ice_level; - -vec3 rainbow(float d) -{ - // d is the dot product of view and sun directions, so ranging -1.0..1.0 - // 'interesting' values of d are the range -0.75..-0.825, when view is nearly opposite of sun vec - // Rainbox texture mode is GL_REPEAT, so tc of -.75 is equiv to 0.25, -0.825 equiv to 0.175. - - // SL-13629 Rainbow texture has colors within the correct .175...250 range, but order is inverted. - // Rather than replace the texture, we mirror and translate the y tc to keep the colors within the - // interesting range, but in reversed order: i.e. d = (1 - d) - 1.575 - d = clamp(-0.575 - d, 0.0, 1.0); - - // With the colors in the lower 1/4 of the texture, inverting the coords leaves most of it inaccessible. - // So, we can stretch the texcoord above the colors (ie > 0.25) to fill the entire remaining coordinate - // space. This improves gradation, reduces banding within the rainbow interior. (1-0.25) / (0.425/0.25) = 4.2857 - float interior_coord = max(0.0, d - 0.25) * 4.2857; - d = clamp(d, 0.0, 0.25) + interior_coord; - - float rad = (droplet_radius - 5.0f) / 1024.0f; - return pow(texture2D(rainbow_map, vec2(rad, d)).rgb, vec3(1.8)) * moisture_level; -} - -vec3 halo22(float d) -{ - d = clamp(d, 0.1, 1.0); - float v = sqrt(clamp(1 - (d * d), 0, 1)); - return texture2D(halo_map, vec2(0, v)).rgb * ice_level; -} - -/// Soft clips the light with a gamma correction -vec3 scaleSoftClip(vec3 light); - -void main() -{ - // World / view / projection - // Get relative position (offset why?) - vec3 rel_pos = pos.xyz - camPosLocal.xyz + vec3(0, 50, 0); - - // Adj position vector to clamp altitude - if (rel_pos.y > 0.) - { - rel_pos *= (max_y / rel_pos.y); - } - if (rel_pos.y < 0.) - { - rel_pos *= (-32000. / rel_pos.y); - } - - // Normalized - vec3 rel_pos_norm = normalize(rel_pos); - float rel_pos_len = length(rel_pos); - - // Initialize temp variables - vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; - - // Sunlight attenuation effect (hue and brightness) due to atmosphere - // this is used later for sunlight modulation at various altitudes - vec4 light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y); - - // Calculate relative weights - vec4 combined_haze = abs(blue_density) + vec4(abs(haze_density)); - vec4 blue_weight = blue_density / combined_haze; - vec4 haze_weight = haze_density / combined_haze; - - // Compute sunlight from rel_pos & lightnorm (for long rays like sky) - float off_axis = 1.0 / max(1e-6, max(0, rel_pos_norm.y) + lightnorm.y); - sunlight *= exp(-light_atten * off_axis); - - // Distance - float density_dist = rel_pos_len * density_multiplier; - - // Transparency (-> combined_haze) - // ATI Bugfix -- can't store combined_haze*density_dist in a variable because the ati - // compiler gets confused. - combined_haze = exp(-combined_haze * density_dist); - - // Compute haze glow - float haze_glow = dot(rel_pos_norm, lightnorm.xyz); - haze_glow = 1. - haze_glow; - // haze_glow is 0 at the sun and increases away from sun - haze_glow = max(haze_glow, .001); - // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) - haze_glow *= glow.x; - // Higher glow.x gives dimmer glow (because next step is 1 / "angle") - haze_glow = pow(haze_glow, glow.z); - // glow.z should be negative, so we're doing a sort of (1 / "angle") function - - // Add "minimum anti-solar illumination" - // For sun, add to glow. For moon, remove glow entirely. SL-13768 - haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (sun_moon_glow_factor * (haze_glow + 0.25)); - - // Haze color above cloud - vec4 color = blue_horizon * blue_weight * (sunlight + ambient_color) - + haze_horizon * haze_weight * (sunlight * haze_glow + ambient_color); - - // Final atmosphere additive - color *= (1. - combined_haze); - - // Increase ambient when there are more clouds - // TODO 9/20: DJH what does this do? max(0,(1-ambient)) will change the color - vec4 ambient = ambient_color + max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5; - - // Dim sunlight by cloud shadow percentage - sunlight *= max(0.0, (1. - cloud_shadow)); - - // Haze color below cloud - vec4 add_below_cloud = blue_horizon * blue_weight * (sunlight + ambient) - + haze_horizon * haze_weight * (sunlight * haze_glow + ambient); - - // Attenuate cloud color by atmosphere - combined_haze = sqrt(combined_haze); // less atmos opacity (more transparency) below clouds - - // At horizon, blend high altitude sky color towards the darker color below the clouds - color += (add_below_cloud - color) * (1. - sqrt(combined_haze)); - - float optic_d = dot(rel_pos_norm, lightnorm.xyz); - vec3 halo_22 = halo22(optic_d); - color.rgb += rainbow(optic_d); - color.rgb += halo_22; - color.rgb *= 2.; - color.rgb = scaleSoftClip(color.rgb); - - // Gamma correct for WL (soft clip effect). - frag_data[0] = vec4(color.rgb, 1.0); - frag_data[1] = vec4(0.0, 0.0, 0.0, 0.0); - frag_data[2] = vec4(0.0, 0.0, 0.0, 1.0); // 1.0 in norm.w masks off fog -} diff --git a/indra/newview/app_settings/shaders/class2/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class2/deferred/skyV.glsl deleted file mode 100644 index bcf775577a..0000000000 --- a/indra/newview/app_settings/shaders/class2/deferred/skyV.glsl +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file WLSkyV.glsl - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2005, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -uniform mat4 modelview_projection_matrix; - -ATTRIBUTE vec3 position; - -// SKY //////////////////////////////////////////////////////////////////////// -// The vertex shader for creating the atmospheric sky -/////////////////////////////////////////////////////////////////////////////// - -VARYING vec3 pos; - -void main() -{ - - // World / view / projection - pos = position.xyz; - gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); -} diff --git a/indra/newview/llbuycurrencyhtml.cpp b/indra/newview/llbuycurrencyhtml.cpp index 1c69dadb12..7ad06f8eaa 100644 --- a/indra/newview/llbuycurrencyhtml.cpp +++ b/indra/newview/llbuycurrencyhtml.cpp @@ -41,7 +41,7 @@ class LLBuyCurrencyHTMLHandler : { public: // requests will be throttled from a non-trusted browser - LLBuyCurrencyHTMLHandler() : LLCommandHandler( "buycurrencyhtml", UNTRUSTED_ALLOW ) {} + LLBuyCurrencyHTMLHandler() : LLCommandHandler( "buycurrencyhtml", UNTRUSTED_THROTTLE) {} bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp index 23e2271eae..74f37961c7 100644 --- a/indra/newview/llcommandhandler.cpp +++ b/indra/newview/llcommandhandler.cpp @@ -39,6 +39,7 @@ #define THROTTLE_PERIOD 5 // required seconds between throttled commands static LLCommandDispatcherListener sCommandDispatcherListener; +const std::string LLCommandHandler::NAV_TYPE_CLICKED = "clicked"; //--------------------------------------------------------------------------- // Underlying registry for command handlers, not directly accessible. @@ -64,6 +65,9 @@ public: bool trusted_browser); private: + void notifySlurlBlocked(); + void notifySlurlThrottled(); + friend LLSD LLCommandDispatcher::enumerate(); std::map<std::string, LLCommandHandlerInfo> mMap; }; @@ -96,8 +100,6 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, const std::string& nav_type, bool trusted_browser) { - static bool slurl_blocked = false; - static bool slurl_throttled = false; static F64 last_throttle_time = 0.0; F64 cur_time = 0.0; std::map<std::string, LLCommandHandlerInfo>::iterator it = mMap.find(cmd); @@ -115,44 +117,45 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, // block request from external browser, but report as // "handled" because it was well formatted. LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL; - if (! slurl_blocked) - { - if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT) - { - // Note: commands can arrive before we initialize everything we need for Notification. - LLNotificationsUtil::add("BlockedSLURL"); - } - slurl_blocked = true; - } + notifySlurlBlocked(); return true; + case LLCommandHandler::UNTRUSTED_CLICK_ONLY: + if (nav_type == LLCommandHandler::NAV_TYPE_CLICKED + && info.mHandler->canHandleUntrusted(params, query_map, web, nav_type)) + { + break; + } + LL_WARNS_ONCE("SLURL") << "Blocked SLURL click-only command " << cmd << " from untrusted browser" << LL_ENDL; + notifySlurlBlocked(); + return true; + case LLCommandHandler::UNTRUSTED_THROTTLE: + //skip initial request from external browser before STATE_BROWSER_INIT + if (LLStartUp::getStartupState() == STATE_FIRST) + { + return true; + } + if (!info.mHandler->canHandleUntrusted(params, query_map, web, nav_type)) + { + LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL; + notifySlurlBlocked(); + return true; + } // if users actually click on a link, we don't need to throttle it // (throttling mechanism is used to prevent an avalanche of clicks via // javascript - if ( nav_type == "clicked" ) + if (nav_type == LLCommandHandler::NAV_TYPE_CLICKED) { break; } - //skip initial request from external browser before STATE_BROWSER_INIT - if (LLStartUp::getStartupState() == STATE_FIRST) - { - return true; - } cur_time = LLTimer::getElapsedSeconds(); if (cur_time < last_throttle_time + THROTTLE_PERIOD) { // block request from external browser if it happened // within THROTTLE_PERIOD seconds of the last command LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL; - if (! slurl_throttled) - { - if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT) - { - LLNotificationsUtil::add("ThrottledSLURL"); - } - slurl_throttled = true; - } + notifySlurlThrottled(); return true; } last_throttle_time = cur_time; @@ -163,6 +166,34 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, return info.mHandler->handle(params, query_map, web); } +void LLCommandHandlerRegistry::notifySlurlBlocked() +{ + static bool slurl_blocked = false; + if (!slurl_blocked) + { + if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT) + { + // Note: commands can arrive before we initialize everything we need for Notification. + LLNotificationsUtil::add("BlockedSLURL"); + } + slurl_blocked = true; + } +} + +void LLCommandHandlerRegistry::notifySlurlThrottled() +{ + static bool slurl_throttled = false; + if (!slurl_throttled) + { + if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT) + { + // Note: commands can arrive before we initialize everything we need for Notification. + LLNotificationsUtil::add("ThrottledSLURL"); + } + slurl_throttled = true; + } +} + //--------------------------------------------------------------------------- // Automatic registration of commands, runs before main() //--------------------------------------------------------------------------- @@ -230,6 +261,7 @@ symbol_info symbols[] = { ent(LLCommandHandler::UNTRUSTED_ALLOW), // allow commands from untrusted browsers ent(LLCommandHandler::UNTRUSTED_BLOCK), // ignore commands from untrusted browsers + ent(LLCommandHandler::UNTRUSTED_CLICK_ONLY), // allow untrusted, but only if clicked ent(LLCommandHandler::UNTRUSTED_THROTTLE) // allow untrusted, but only a few per min. }; diff --git a/indra/newview/llcommandhandler.h b/indra/newview/llcommandhandler.h index 1e0895565a..763e3ee51f 100644 --- a/indra/newview/llcommandhandler.h +++ b/indra/newview/llcommandhandler.h @@ -65,9 +65,12 @@ public: { UNTRUSTED_ALLOW, // allow commands from untrusted browsers UNTRUSTED_BLOCK, // ignore commands from untrusted browsers + UNTRUSTED_CLICK_ONLY, // allow untrusted, but only if clicked UNTRUSTED_THROTTLE // allow untrusted, but only a few per min. }; + static const std::string NAV_TYPE_CLICKED; + LLCommandHandler(const char* command, EUntrustedAccess untrusted_access); // Automatically registers object to get called when // command is executed. All commands can be processed @@ -76,6 +79,13 @@ public: virtual ~LLCommandHandler(); + virtual bool canHandleUntrusted( + const LLSD& params, + const LLSD& query_map, + LLMediaCtrl* web, + const std::string& nav_type) + { return true; } + virtual bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) = 0; diff --git a/indra/newview/llfloater360capture.cpp b/indra/newview/llfloater360capture.cpp index c075f7e8bd..8ad0bb3995 100644 --- a/indra/newview/llfloater360capture.cpp +++ b/indra/newview/llfloater360capture.cpp @@ -84,7 +84,7 @@ LLFloater360Capture::~LLFloater360Capture() // Tell the Simulator not to send us everything anymore // and revert to the regular "keyhole" frustum of interest // list updates. - if (gSavedSettings.getBOOL("360CaptureUseInterestListCap")) + if (gSavedSettings.getBOOL("360CaptureUseInterestListCap") && !LLApp::isExiting()) { const bool send_everything = false; changeInterestListMode(send_everything); @@ -537,7 +537,8 @@ void LLFloater360Capture::capture360Images() // We need to convert from the angle getYaw() gives us into something // the XMP data field wants (N=0, E=90, S=180, W= 270 etc.) mInitialHeadingDeg = (360 + 90 - (int)(camera->getYaw() * RAD_TO_DEG)) % 360; - LL_INFOS("360Capture") << "Recording a heading of " << (int)(mInitialHeadingDeg) << LL_ENDL; + LL_INFOS("360Capture") << "Recording a heading of " << (int)(mInitialHeadingDeg) + << " Image size: " << (S32)mSourceImageSize << LL_ENDL; // camera constants for the square, cube map capture image camera->setAspect(1.0); // must set aspect ratio first to avoid undesirable clamping of vertical FoV @@ -587,6 +588,9 @@ void LLFloater360Capture::capture360Images() // for each of the 6 directions we shoot... for (int i = 0; i < 6; i++) { + LLAppViewer::instance()->pauseMainloopTimeout(); + LLViewerStats::instance().getRecording().stop(); + // these buffers are where the raw, captured pixels are stored and // the first time we use them, we have to make a new one if (mRawImages[i] == nullptr) @@ -624,8 +628,10 @@ void LLFloater360Capture::capture360Images() auto duration = std::chrono::duration_cast<std::chrono::duration<double>>(t_end - t_start); encode_time_total += duration.count(); - // ping the main loop in case the snapshot process takes a really long - // time and we get disconnected + LLViewerStats::instance().getRecording().resume(); + LLAppViewer::instance()->resumeMainloopTimeout(); + + // update main loop timeout state LLAppViewer::instance()->pingMainloopTimeout("LLFloater360Capture::capture360Images"); } diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp index 2a1749bd42..a682064dad 100644 --- a/indra/newview/llfloateropenobject.cpp +++ b/indra/newview/llfloateropenobject.cpp @@ -56,8 +56,6 @@ LLFloaterOpenObject::LLFloaterOpenObject(const LLSD& key) mDirty(TRUE) { mCommitCallbackRegistrar.add("OpenObject.MoveToInventory", boost::bind(&LLFloaterOpenObject::onClickMoveToInventory, this)); - mCommitCallbackRegistrar.add("OpenObject.MoveAndWear", boost::bind(&LLFloaterOpenObject::onClickMoveAndWear, this)); - mCommitCallbackRegistrar.add("OpenObject.ReplaceOutfit", boost::bind(&LLFloaterOpenObject::onClickReplace, this)); mCommitCallbackRegistrar.add("OpenObject.Cancel", boost::bind(&LLFloaterOpenObject::onClickCancel, this)); } @@ -243,18 +241,6 @@ void LLFloaterOpenObject::onClickMoveToInventory() closeFloater(); } -void LLFloaterOpenObject::onClickMoveAndWear() -{ - moveToInventory(true, false); - closeFloater(); -} - -void LLFloaterOpenObject::onClickReplace() -{ - moveToInventory(true, true); - closeFloater(); -} - void LLFloaterOpenObject::onClickCancel() { closeFloater(); diff --git a/indra/newview/llfloateropenobject.h b/indra/newview/llfloateropenobject.h index 2e761f99bf..745753316b 100644 --- a/indra/newview/llfloateropenobject.h +++ b/indra/newview/llfloateropenobject.h @@ -63,8 +63,6 @@ protected: void moveToInventory(bool wear, bool replace = false); void onClickMoveToInventory(); - void onClickMoveAndWear(); - void onClickReplace(); void onClickCancel(); static void callbackCreateInventoryCategory(const LLUUID& category_id, LLUUID object_id, bool wear, bool replace = false); static void callbackMoveInventory(S32 result, void* data); diff --git a/indra/newview/llfloaterpathfindinglinksets.cpp b/indra/newview/llfloaterpathfindinglinksets.cpp index 1e46d7a402..03aede94c6 100644 --- a/indra/newview/llfloaterpathfindinglinksets.cpp +++ b/indra/newview/llfloaterpathfindinglinksets.cpp @@ -44,6 +44,7 @@ #include "llpathfindinglinkset.h" #include "llpathfindinglinksetlist.h" #include "llpathfindingmanager.h" +#include "llsearcheditor.h" #include "llscrolllistitem.h" #include "llsd.h" #include "lltextbase.h" @@ -114,17 +115,13 @@ BOOL LLFloaterPathfindingLinksets::postBuild() { mBeaconColor = LLUIColorTable::getInstance()->getColor("PathfindingLinksetBeaconColor"); - mFilterByName = findChild<LLLineEditor>("filter_by_name"); - llassert(mFilterByName != NULL); - mFilterByName->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyAllFilters, this)); - mFilterByName->setSelectAllonFocusReceived(true); - mFilterByName->setCommitOnFocusLost(true); - - mFilterByDescription = findChild<LLLineEditor>("filter_by_description"); - llassert(mFilterByDescription != NULL); - mFilterByDescription->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyAllFilters, this)); - mFilterByDescription->setSelectAllonFocusReceived(true); - mFilterByDescription->setCommitOnFocusLost(true); + mFilterByName = getChild<LLSearchEditor>("filter_by_name"); + mFilterByName->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyAllFilters, this)); + mFilterByName->setCommitOnFocusLost(true); + + mFilterByDescription = getChild<LLSearchEditor>("filter_by_description"); + mFilterByDescription->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyAllFilters, this)); + mFilterByDescription->setCommitOnFocusLost(true); mFilterByLinksetUse = findChild<LLComboBox>("filter_by_linkset_use"); llassert(mFilterByLinksetUse != NULL); diff --git a/indra/newview/llfloaterpathfindinglinksets.h b/indra/newview/llfloaterpathfindinglinksets.h index 7149da9215..a954d8a8ec 100644 --- a/indra/newview/llfloaterpathfindinglinksets.h +++ b/indra/newview/llfloaterpathfindinglinksets.h @@ -42,6 +42,7 @@ class LLSD; class LLTextBase; class LLUICtrl; class LLVector3; +class LLSearchEditor; class LLFloaterPathfindingLinksets : public LLFloaterPathfindingObjects { @@ -105,8 +106,8 @@ private: LLPathfindingLinkset::ELinksetUse convertToLinksetUse(LLSD pXuiValue) const; LLSD convertToXuiValue(LLPathfindingLinkset::ELinksetUse pLinksetUse) const; - LLLineEditor *mFilterByName; - LLLineEditor *mFilterByDescription; + LLSearchEditor *mFilterByName; + LLSearchEditor *mFilterByDescription; LLComboBox *mFilterByLinksetUse; LLComboBox *mEditLinksetUse; LLScrollListItem *mEditLinksetUseUnset; diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index bb3ed77772..7e6af45515 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -45,7 +45,7 @@ class LLSearchHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLSearchHandler() : LLCommandHandler("search", UNTRUSTED_THROTTLE) { } + LLSearchHandler() : LLCommandHandler("search", UNTRUSTED_CLICK_ONLY) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableSearch")) diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 977023cfe4..4f772aa141 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -123,7 +123,7 @@ class LLWorldMapHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_THROTTLE ) { } + LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_CLICK_ONLY ) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) @@ -160,7 +160,7 @@ class LLMapTrackAvatarHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLMapTrackAvatarHandler() : LLCommandHandler("maptrackavatar", UNTRUSTED_THROTTLE) + LLMapTrackAvatarHandler() : LLCommandHandler("maptrackavatar", UNTRUSTED_CLICK_ONLY) { } diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 84a1278767..815840f990 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -52,7 +52,32 @@ class LLGroupHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_THROTTLE) { } + LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_CLICK_ONLY) { } + + virtual bool canHandleUntrusted( + const LLSD& params, + const LLSD& query_map, + LLMediaCtrl* web, + const std::string& nav_type) + { + if (params.size() < 1) + { + return true; // don't block, will fail later + } + + if (nav_type == NAV_TYPE_CLICKED) + { + return true; + } + + const std::string verb = params[0].asString(); + if (verb == "create") + { + return false; + } + return true; + } + bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llmaterialmgr.cpp b/indra/newview/llmaterialmgr.cpp index 11aa607393..6e1e6506d9 100644 --- a/indra/newview/llmaterialmgr.cpp +++ b/indra/newview/llmaterialmgr.cpp @@ -429,12 +429,10 @@ void LLMaterialMgr::onGetResponse(bool success, const LLSD& content, const LLUUI llassert(content.has(MATERIALS_CAP_ZIP_FIELD)); llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary()); - LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); - std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size()); - std::istringstream content_stream(content_string); + const LLSD::Binary& content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); LLSD response_data; - U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_stream, content_binary.size()); + U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_binary.data(), content_binary.size()); if (uzip_result != LLUZipHelper::ZR_OK) { LL_WARNS("Materials") << "Cannot unzip LLSD binary content: " << uzip_result << LL_ENDL; @@ -472,12 +470,10 @@ void LLMaterialMgr::onGetAllResponse(bool success, const LLSD& content, const LL llassert(content.has(MATERIALS_CAP_ZIP_FIELD)); llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary()); - LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); - std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size()); - std::istringstream content_stream(content_string); + const LLSD::Binary& content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); LLSD response_data; - U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_stream, content_binary.size()); + U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_binary.data(), content_binary.size()); if (uzip_result != LLUZipHelper::ZR_OK) { LL_WARNS("Materials") << "Cannot unzip LLSD binary content: " << uzip_result << LL_ENDL; @@ -541,12 +537,10 @@ void LLMaterialMgr::onPutResponse(bool success, const LLSD& content) llassert(content.has(MATERIALS_CAP_ZIP_FIELD)); llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary()); - LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); - std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size()); - std::istringstream content_stream(content_string); + const LLSD::Binary& content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); LLSD response_data; - U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_stream, content_binary.size()); + U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_binary.data(), content_binary.size()); if (uzip_result != LLUZipHelper::ZR_OK) { LL_WARNS("Materials") << "Cannot unzip LLSD binary content: " << uzip_result << LL_ENDL; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 4dd0543693..f937754368 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -77,6 +77,8 @@ #include "lluploaddialog.h" #include "llfloaterreg.h" +#include "boost/iostreams/device/array.hpp" +#include "boost/iostreams/stream.hpp" #include "boost/lexical_cast.hpp" #ifndef LL_WINDOWS @@ -138,7 +140,7 @@ // data copied // headerReceived() invoked // LLSD parsed -// mMeshHeader, mMeshHeaderSize updated +// mMeshHeader updated // scan mPendingLOD for LOD request // push LODRequest to mLODReqQ // ... @@ -246,7 +248,6 @@ // sActiveLODRequests mMutex rw.any.mMutex, ro.repo.none [1] // sMaxConcurrentRequests mMutex wo.main.none, ro.repo.none, ro.main.mMutex // mMeshHeader mHeaderMutex rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0] -// mMeshHeaderSize mHeaderMutex rw.repo.mHeaderMutex // mSkinRequests mMutex rw.repo.mMutex, ro.repo.none [5] // mSkinInfoQ mMutex rw.repo.mMutex, rw.main.mMutex [5] (was: [0]) // mDecompositionRequests mMutex rw.repo.mMutex, ro.repo.none [5] @@ -858,6 +859,12 @@ LLMeshRepoThread::~LLMeshRepoThread() mHttpRequestSet.clear(); mHttpHeaders.reset(); + while (!mSkinInfoQ.empty()) + { + delete mSkinInfoQ.front(); + mSkinInfoQ.pop_front(); + } + while (!mDecompositionQ.empty()) { delete mDecompositionQ.front(); @@ -947,7 +954,8 @@ void LLMeshRepoThread::run() else { // too many fails - mUnavailableQ.push(req); + LLMutexLock lock(mMutex); + mUnavailableQ.push_back(req); LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL; } } @@ -1023,37 +1031,42 @@ void LLMeshRepoThread::run() if (!mSkinRequests.empty()) { - std::set<UUIDBasedRequest> incomplete; - while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) - { - mMutex->lock(); - std::set<UUIDBasedRequest>::iterator iter = mSkinRequests.begin(); - UUIDBasedRequest req = *iter; - mSkinRequests.erase(iter); - mMutex->unlock(); - if (req.isDelayed()) - { - incomplete.insert(req); - } - else if (!fetchMeshSkinInfo(req.mId)) - { - if (req.canRetry()) - { - req.updateTime(); - incomplete.insert(req); - } - else - { - LL_DEBUGS() << "mSkinRequests failed: " << req.mId << LL_ENDL; - } - } - } + std::list<UUIDBasedRequest> incomplete; + while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) + { - if (!incomplete.empty()) - { - LLMutexLock locker(mMutex); - mSkinRequests.insert(incomplete.begin(), incomplete.end()); - } + mMutex->lock(); + auto req = mSkinRequests.front(); + mSkinRequests.pop_front(); + mMutex->unlock(); + if (req.isDelayed()) + { + incomplete.emplace_back(req); + } + else if (!fetchMeshSkinInfo(req.mId, req.canRetry())) + { + if (req.canRetry()) + { + req.updateTime(); + incomplete.emplace_back(req); + } + else + { + LLMutexLock locker(mMutex); + mSkinUnavailableQ.push_back(req); + LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; + } + } + } + + if (!incomplete.empty()) + { + LLMutexLock locker(mMutex); + for (const auto& req : incomplete) + { + mSkinRequests.push_back(req); + } + } } // holding lock, try next list @@ -1152,7 +1165,7 @@ void LLMeshRepoThread::run() // Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id) { - mSkinRequests.insert(UUIDBasedRequest(mesh_id)); + mSkinRequests.push_back(UUIDBasedRequest(mesh_id)); } // Mutex: LLMeshRepoThread::mMutex must be held on entry @@ -1178,10 +1191,13 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { //could be called from any thread + const LLUUID& mesh_id = mesh_params.getSculptID(); LLMutexLock lock(mMutex); - mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID()); + LLMutexLock header_lock(mHeaderMutex); + mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); if (iter != mMeshHeader.end()) { //if we have the header, request LOD byte range + LODRequest req(mesh_params, lod); { mLODReqQ.push(req); @@ -1191,8 +1207,7 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) else { HeaderRequest req(mesh_params); - - pending_lod_map::iterator pending = mPendingLOD.find(mesh_params); + pending_lod_map::iterator pending = mPendingLOD.find(mesh_id); if (pending != mPendingLOD.end()) { //append this lod request to existing header request @@ -1202,7 +1217,7 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) else { //if no header request is pending, fetch header mHeaderReqQ.push(req); - mPendingLOD[mesh_params].push_back(lod); + mPendingLOD[mesh_id].push_back(lod); } } } @@ -1307,7 +1322,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, } -bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) +bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) { if (!mHeaderMutex) @@ -1317,7 +1332,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) mHeaderMutex->lock(); - if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) + auto header_it = mMeshHeader.find(mesh_id); + if (header_it == mMeshHeader.end()) { //we have no header info for this mesh, do nothing mHeaderMutex->unlock(); return false; @@ -1325,13 +1341,14 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) ++LLMeshRepository::sMeshRequestCount; bool ret = true; - U32 header_size = mMeshHeaderSize[mesh_id]; + U32 header_size = header_it->second.first; if (header_size > 0) { - S32 version = mMeshHeader[mesh_id]["version"].asInteger(); - S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger(); + const LLSD& header = header_it->second.second; + S32 version = header["version"].asInteger(); + S32 offset = header_size + header["skin"]["offset"].asInteger(); + S32 size = header["skin"]["size"].asInteger(); mHeaderMutex->unlock(); @@ -1387,13 +1404,28 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) << LL_ENDL; ret = false; } - else + else if(can_retry) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); } + else + { + LLMutexLock locker(mMutex); + mSkinUnavailableQ.emplace_back(mesh_id); + } + } + else + { + LLMutexLock locker(mMutex); + mSkinUnavailableQ.emplace_back(mesh_id); } } + else + { + LLMutexLock locker(mMutex); + mSkinUnavailableQ.emplace_back(mesh_id); + } } else { @@ -1413,21 +1445,23 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) mHeaderMutex->lock(); - if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) + auto header_it = mMeshHeader.find(mesh_id); + if (header_it == mMeshHeader.end()) { //we have no header info for this mesh, do nothing mHeaderMutex->unlock(); return false; } ++LLMeshRepository::sMeshRequestCount; - U32 header_size = mMeshHeaderSize[mesh_id]; + U32 header_size = header_it->second.first; bool ret = true; if (header_size > 0) { - S32 version = mMeshHeader[mesh_id]["version"].asInteger(); - S32 offset = header_size + mMeshHeader[mesh_id]["physics_convex"]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id]["physics_convex"]["size"].asInteger(); + const auto& header = header_it->second.second; + S32 version = header["version"].asInteger(); + S32 offset = header_size + header["physics_convex"]["offset"].asInteger(); + S32 size = header["physics_convex"]["size"].asInteger(); mHeaderMutex->unlock(); @@ -1510,21 +1544,23 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) mHeaderMutex->lock(); - if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) + auto header_it = mMeshHeader.find(mesh_id); + if (header_it == mMeshHeader.end()) { //we have no header info for this mesh, do nothing mHeaderMutex->unlock(); return false; } ++LLMeshRepository::sMeshRequestCount; - U32 header_size = mMeshHeaderSize[mesh_id]; + U32 header_size = header_it->second.first; bool ret = true; if (header_size > 0) { - S32 version = mMeshHeader[mesh_id]["version"].asInteger(); - S32 offset = header_size + mMeshHeader[mesh_id]["physics_mesh"]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id]["physics_mesh"]["size"].asInteger(); + const auto& header = header_it->second.second; + S32 version = header["version"].asInteger(); + S32 offset = header_size + header["physics_mesh"]["offset"].asInteger(); + S32 size = header["physics_mesh"]["size"].asInteger(); mHeaderMutex->unlock(); @@ -1704,20 +1740,25 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, return false; } - mHeaderMutex->lock(); + const LLUUID& mesh_id = mesh_params.getSculptID(); + mHeaderMutex->lock(); + auto header_it = mMeshHeader.find(mesh_id); + if (header_it == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } ++LLMeshRepository::sMeshRequestCount; bool retval = true; - - LLUUID mesh_id = mesh_params.getSculptID(); - U32 header_size = mMeshHeaderSize[mesh_id]; - + U32 header_size = header_it->second.first; if (header_size > 0) { - S32 version = mMeshHeader[mesh_id]["version"].asInteger(); - S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger(); + const auto& header = header_it->second.second; + S32 version = header["version"].asInteger(); + S32 offset = header_size + header[header_lod[lod]]["offset"].asInteger(); + S32 size = header[header_lod[lod]]["size"].asInteger(); mHeaderMutex->unlock(); if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) @@ -1792,17 +1833,20 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, } else { - mUnavailableQ.push(LODRequest(mesh_params, lod)); + LLMutexLock lock(mMutex); + mUnavailableQ.push_back(LODRequest(mesh_params, lod)); } } else { - mUnavailableQ.push(LODRequest(mesh_params, lod)); + LLMutexLock lock(mMutex); + mUnavailableQ.push_back(LODRequest(mesh_params, lod)); } } else { - mUnavailableQ.push(LODRequest(mesh_params, lod)); + LLMutexLock lock(mMutex); + mUnavailableQ.push_back(LODRequest(mesh_params, lod)); } } else @@ -1821,27 +1865,12 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes U32 header_size = 0; if (data_size > 0) { - std::istringstream stream; - try - { - std::string res_str((char*)data, data_size); + U32 dsize = data_size; + char* result_ptr = strip_deprecated_header((char*)data, dsize, &header_size); - std::string deprecated_header("<? LLSD/Binary ?>"); + data_size = dsize; - if (res_str.substr(0, deprecated_header.size()) == deprecated_header) - { - res_str = res_str.substr(deprecated_header.size() + 1, data_size); - header_size = deprecated_header.size() + 1; - } - data_size = res_str.size(); - - stream.str(res_str); - } - catch (std::bad_alloc&) - { - // out of memory, we won't be able to process this mesh - return MESH_OUT_OF_MEMORY; - } + boost::iostreams::stream<boost::iostreams::array_source> stream(result_ptr, data_size); if (!LLSDSerialize::fromBinary(header, stream, data_size)) { @@ -1878,8 +1907,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes { LLMutexLock lock(mHeaderMutex); - mMeshHeaderSize[mesh_id] = header_size; - mMeshHeader[mesh_id] = header; + mMeshHeader[mesh_id] = { header_size, header }; LLMeshRepository::sCacheBytesHeaders += header_size; } @@ -1887,7 +1915,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. //check for pending requests - pending_lod_map::iterator iter = mPendingLOD.find(mesh_params); + pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); if (iter != mPendingLOD.end()) { for (U32 i = 0; i < iter->second.size(); ++i) @@ -1911,26 +1939,14 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p } LLPointer<LLVolume> volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod)); - std::istringstream stream; - try - { - std::string mesh_string((char*)data, data_size); - stream.str(mesh_string); - } - catch (std::bad_alloc&) - { - // out of memory, we won't be able to process this mesh - return MESH_OUT_OF_MEMORY; - } - - if (volume->unpackVolumeFaces(stream, data_size)) + if (volume->unpackVolumeFaces(data, data_size)) { if (volume->getNumFaces() > 0) { LoadedMesh mesh(volume, mesh_params, lod); { LLMutexLock lock(mMutex); - mLoadedQ.push(mesh); + mLoadedQ.push_back(mesh); // LLPointer is not thread safe, since we added this pointer into // threaded list, make sure counter gets decreased inside mutex lock // and won't affect mLoadedQ processing @@ -1953,10 +1969,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat { try { - std::string res_str((char*)data, data_size); - std::istringstream stream(res_str); - - U32 uzip_result = LLUZipHelper::unzip_llsd(skin, stream, data_size); + U32 uzip_result = LLUZipHelper::unzip_llsd(skin, data, data_size); if (uzip_result != LLUZipHelper::ZR_OK) { LL_WARNS(LOG_MESH) << "Mesh skin info parse error. Not a valid mesh asset! ID: " << mesh_id @@ -1973,8 +1986,16 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat } { - LLMeshSkinInfo info(skin); - info.mMeshID = mesh_id; + LLMeshSkinInfo* info = nullptr; + try + { + info = new LLMeshSkinInfo(mesh_id, skin); + } + catch (const std::bad_alloc& ex) + { + LL_WARNS() << "Failed to allocate skin info with exception: " << ex.what() << LL_ENDL; + return false; + } // LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL; { @@ -1994,10 +2015,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 { try { - std::string res_str((char*)data, data_size); - std::istringstream stream(res_str); - - U32 uzip_result = LLUZipHelper::unzip_llsd(decomp, stream, data_size); + U32 uzip_result = LLUZipHelper::unzip_llsd(decomp, data, data_size); if (uzip_result != LLUZipHelper::ZR_OK) { LL_WARNS(LOG_MESH) << "Mesh decomposition parse error. Not a valid mesh asset! ID: " << mesh_id @@ -2006,7 +2024,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 return false; } } - catch (std::bad_alloc&) + catch (const std::bad_alloc&) { LL_WARNS(LOG_MESH) << "Out of memory for mesh ID " << mesh_id << " of size: " << data_size << LL_ENDL; return false; @@ -2043,32 +2061,8 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_ volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH); LLPointer<LLVolume> volume = new LLVolume(volume_params,0); - std::istringstream stream; - try - { - std::string mesh_string((char*)data, data_size); - stream.str(mesh_string); - } - catch (std::bad_alloc&) - { - // out of memory, we won't be able to process this mesh - delete d; - return MESH_OUT_OF_MEMORY; - } - - if (volume->unpackVolumeFaces(stream, data_size)) + if (volume->unpackVolumeFaces(data, data_size)) { - //load volume faces into decomposition buffer - S32 vertex_count = 0; - S32 index_count = 0; - - for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i) - { - const LLVolumeFace& face = volume->getVolumeFace(i); - vertex_count += face.mNumVertices; - index_count += face.mNumIndices; - } - d->mPhysicsShapeMesh.clear(); std::vector<LLVector3>& pos = d->mPhysicsShapeMesh.mPositions; @@ -2881,58 +2875,72 @@ void LLMeshRepoThread::notifyLoadedMeshes() return; } - while (!mLoadedQ.empty()) + if (!mLoadedQ.empty()) { + std::deque<LoadedMesh> loaded_queue; + mMutex->lock(); - if (mLoadedQ.empty()) + if (!mLoadedQ.empty()) { + loaded_queue.swap(mLoadedQ); mMutex->unlock(); - break; - } - LoadedMesh mesh = mLoadedQ.front(); // make sure nothing else owns volume pointer by this point - mLoadedQ.pop(); - mMutex->unlock(); - - update_metrics = true; - if (mesh.mVolume->getNumVolumeFaces() > 0) - { - gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); - } - else - { - gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, - LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); + + update_metrics = true; + + // Process the elements free of the lock + for (const auto& mesh : loaded_queue) + { + if (mesh.mVolume->getNumVolumeFaces() > 0) + { + gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); + } + else + { + gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, + LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); + } + } } } - while (!mUnavailableQ.empty()) + if (!mUnavailableQ.empty()) { + std::deque<LODRequest> unavil_queue; + mMutex->lock(); - if (mUnavailableQ.empty()) + if (!mUnavailableQ.empty()) { + unavil_queue.swap(mUnavailableQ); mMutex->unlock(); - break; - } - - LODRequest req = mUnavailableQ.front(); - mUnavailableQ.pop(); - mMutex->unlock(); - update_metrics = true; - gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); + update_metrics = true; + + // Process the elements free of the lock + for (const auto& req : unavil_queue) + { + gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); + } + } } - if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty()) + if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty()) { if (mMutex->trylock()) { - std::list<LLMeshSkinInfo> skin_info_q; + std::deque<LLMeshSkinInfo*> skin_info_q; + std::deque<UUIDBasedRequest> skin_info_unavail_q; std::list<LLModel::Decomposition*> decomp_q; if (! mSkinInfoQ.empty()) { skin_info_q.swap(mSkinInfoQ); } + + if (! mSkinUnavailableQ.empty()) + { + skin_info_unavail_q.swap(mSkinUnavailableQ); + } + if (! mDecompositionQ.empty()) { decomp_q.swap(mDecompositionQ); @@ -2946,6 +2954,11 @@ void LLMeshRepoThread::notifyLoadedMeshes() gMeshRepo.notifySkinInfoReceived(skin_info_q.front()); skin_info_q.pop_front(); } + while (! skin_info_unavail_q.empty()) + { + gMeshRepo.notifySkinInfoUnavailable(skin_info_unavail_q.front().mId); + skin_info_unavail_q.pop_front(); + } while (! decomp_q.empty()) { @@ -2970,7 +2983,7 @@ S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo if (iter != mMeshHeader.end()) { - LLSD& header = iter->second; + LLSD& header = iter->second.second; return LLMeshRepository::getActualMeshLOD(header, lod); } @@ -3173,7 +3186,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) LLMutexLock lock(gMeshRepo.mThread->mMutex); for (int i(0); i < 4; ++i) { - gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i)); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); } } @@ -3202,7 +3215,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b LLMutexLock lock(gMeshRepo.mThread->mMutex); for (int i(0); i < 4; ++i) { - gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i)); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); } } else if (data && data_size > 0) @@ -3215,8 +3228,8 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b LLMeshRepoThread::mesh_header_map::iterator iter = gMeshRepo.mThread->mMeshHeader.find(mesh_id); if (iter != gMeshRepo.mThread->mMeshHeader.end()) { - header_bytes = (S32)gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; - header = iter->second; + header_bytes = (S32)iter->second.first; + header = iter->second.second; } if (header_bytes > 0 @@ -3284,7 +3297,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b LLMutexLock lock(gMeshRepo.mThread->mMutex); for (int i(0); i < 4; ++i) { - gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i)); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); } } } @@ -3311,7 +3324,7 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) << LL_ENDL; LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, @@ -3348,7 +3361,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body << " Not retrying." << LL_ENDL; LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } } else @@ -3359,7 +3372,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body << " Data size: " << data_size << LL_ENDL; LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } } @@ -3377,9 +3390,8 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) << ", Reason: " << status.toString() << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; - - // *TODO: Mark mesh unavailable on error. For now, simply leave - // request unfulfilled rather than retry forever. + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); } void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, @@ -3410,7 +3422,8 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* LL_WARNS(LOG_MESH) << "Error during mesh skin info processing. ID: " << mMeshID << ", Unknown reason. Not retrying." << LL_ENDL; - // *TODO: Mark mesh unavailable on error + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); } } @@ -3519,7 +3532,7 @@ LLMeshRepository::LLMeshRepository() mMeshThreadCount(0), mThread(NULL) { - + mSkinInfoCullTimer.resetWithExpiry(10.f); } void LLMeshRepository::init() @@ -3620,6 +3633,22 @@ S32 LLMeshRepository::update() return size ; } +void LLMeshRepository::unregisterMesh(LLVOVolume* vobj) +{ + for (auto& lod : mLoadingMeshes) + { + for (auto& param : lod) + { + vector_replace_with_last(param.second, vobj); + } + } + + for (auto& skin_pair : mLoadingSkins) + { + vector_replace_with_last(skin_pair.second, vobj); + } +} + S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod) { LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH); @@ -3635,15 +3664,19 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para { LLMutexLock lock(mMeshMutex); //add volume to list of loading meshes - mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_params); + const auto& mesh_id = mesh_params.getSculptID(); + mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_id); if (iter != mLoadingMeshes[detail].end()) { //request pending for this mesh, append volume id to list - iter->second.insert(vobj->getID()); + auto it = std::find(iter->second.begin(), iter->second.end(), vobj); + if (it == iter->second.end()) { + iter->second.push_back(vobj); + } } else { //first request for this mesh - mLoadingMeshes[detail][mesh_params].insert(vobj->getID()); + mLoadingMeshes[detail][mesh_id].push_back(vobj); mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail)); LLMeshRepository::sLODPending++; } @@ -3800,6 +3833,28 @@ void LLMeshRepository::notifyLoadedMeshes() //call completed callbacks on finished decompositions mDecompThread->notifyCompleted(); + if (mSkinInfoCullTimer.checkExpirationAndReset(10.f)) + { + //// Clean up dead skin info + //U64Bytes skinbytes(0); + for (auto iter = mSkinMap.begin(), ender = mSkinMap.end(); iter != ender;) + { + auto copy_iter = iter++; + + //skinbytes += U64Bytes(sizeof(LLMeshSkinInfo)); + //skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(std::string)); + //skinbytes += U64Bytes(copy_iter->second->mJointNums.size() * sizeof(S32)); + //skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(LLMatrix4a)); + //skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(LLMatrix4)); + + if (copy_iter->second->getNumRefs() == 1) + { + mSkinMap.erase(copy_iter); + } + } + //LL_INFOS() << "Skin info cache elements:" << mSkinMap.size() << " Memory: " << U64Kilobytes(skinbytes) << LL_ENDL; + } + // For major operations, attempt to get the required locks // without blocking and punt if they're not available. The // longest run of holdoffs is kept in sMaxLockHoldoffs just @@ -3870,10 +3925,9 @@ void LLMeshRepository::notifyLoadedMeshes() for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter) { F32 max_score = 0.f; - for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) + for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) { - LLViewerObject* object = gObjectList.findObject(*obj_iter); - + LLVOVolume* object = *obj_iter; if (object) { LLDrawable* drawable = object->mDrawable; @@ -3885,7 +3939,7 @@ void LLMeshRepository::notifyLoadedMeshes() } } - score_map[iter->first.getSculptID()] = max_score; + score_map[iter->first] = max_score; } } @@ -3937,24 +3991,39 @@ void LLMeshRepository::notifyLoadedMeshes() mThread->mSignal->signal(); } -void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info) +void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo* info) { - mSkinMap[info.mMeshID] = info; + mSkinMap[info->mMeshID] = info; // Cache into LLPointer // Alternative: We can get skin size from header - sCacheBytesSkins += info.sizeBytes(); + sCacheBytesSkins += info->sizeBytes(); - skin_load_map::iterator iter = mLoadingSkins.find(info.mMeshID); + skin_load_map::iterator iter = mLoadingSkins.find(info->mMeshID); if (iter != mLoadingSkins.end()) { - for (std::set<LLUUID>::iterator obj_id = iter->second.begin(); obj_id != iter->second.end(); ++obj_id) + for (LLVOVolume* vobj : iter->second) { - LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*obj_id); if (vobj) { - vobj->notifyMeshLoaded(); + vobj->notifySkinInfoLoaded(info); + } + } + mLoadingSkins.erase(iter); + } +} + +void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id) +{ + skin_load_map::iterator iter = mLoadingSkins.find(mesh_id); + if (iter != mLoadingSkins.end()) + { + for (LLVOVolume* vobj : iter->second) + { + if (vobj) + { + vobj->notifySkinInfoUnavailable(); } } - mLoadingSkins.erase(info.mMeshID); + mLoadingSkins.erase(iter); } } @@ -3983,14 +4052,15 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail()); //get list of objects waiting to be notified this mesh is loaded - mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_params); + const auto& mesh_id = mesh_params.getSculptID(); + mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_id); if (volume && obj_iter != mLoadingMeshes[detail].end()) { //make sure target volume is still valid if (volume->getNumVolumeFaces() <= 0) { - LL_WARNS(LOG_MESH) << "Mesh loading returned empty volume. ID: " << mesh_params.getSculptID() + LL_WARNS(LOG_MESH) << "Mesh loading returned empty volume. ID: " << mesh_id << LL_ENDL; } @@ -4004,37 +4074,35 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol } else { - LL_WARNS(LOG_MESH) << "Couldn't find system volume for mesh " << mesh_params.getSculptID() + LL_WARNS(LOG_MESH) << "Couldn't find system volume for mesh " << mesh_id << LL_ENDL; } } //notify waiting LLVOVolume instances that their requested mesh is available - for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter) + for (LLVOVolume* vobj : obj_iter->second) { - LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter); if (vobj) { vobj->notifyMeshLoaded(); } } - mLoadingMeshes[detail].erase(mesh_params); + mLoadingMeshes[detail].erase(obj_iter); } } void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod) { //called from main thread //get list of objects waiting to be notified this mesh is loaded - mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params); - - F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); - + const auto& mesh_id = mesh_params.getSculptID(); + mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id); if (obj_iter != mLoadingMeshes[lod].end()) { - for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter) + F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); + + for (LLVOVolume* vobj : obj_iter->second) { - LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter); if (vobj) { LLVolume* obj_volume = vobj->getVolume(); @@ -4048,7 +4116,7 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, } } - mLoadingMeshes[lod].erase(mesh_params); + mLoadingMeshes[lod].erase(obj_iter); } } @@ -4057,7 +4125,7 @@ S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo return mThread->getActualMeshLOD(mesh_params, lod); } -const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj) +const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj) { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; if (mesh_id.notNull()) @@ -4065,7 +4133,7 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const skin_map::iterator iter = mSkinMap.find(mesh_id); if (iter != mSkinMap.end()) { - return &(iter->second); + return iter->second; } //no skin info known about given mesh, try to fetch it @@ -4074,14 +4142,22 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const LLMutexLock lock(mMeshMutex); //add volume to list of loading meshes skin_load_map::iterator iter = mLoadingSkins.find(mesh_id); - if (iter == mLoadingSkins.end()) - { //no request pending for this skin info + if (iter != mLoadingSkins.end()) + { //request pending for this mesh, append volume id to list + auto it = std::find(iter->second.begin(), iter->second.end(), requesting_obj); + if (it == iter->second.end()) { + iter->second.push_back(requesting_obj); + } + } + else + { + //first request for this mesh + mLoadingSkins[mesh_id].push_back(requesting_obj); mPendingSkinRequests.push(mesh_id); } - mLoadingSkins[mesh_id].insert(requesting_obj->getID()); } } - return NULL; + return nullptr; } void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id) @@ -4184,16 +4260,13 @@ bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id) bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) { LLMutexLock lock(mHeaderMutex); - if (mMeshHeaderSize[mesh_id] > 0) + mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); + if (iter != mMeshHeader.end() && iter->second.first > 0) { - mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); - if (iter != mMeshHeader.end()) + LLSD &mesh = iter->second.second; + if (mesh.has("physics_mesh") && mesh["physics_mesh"].has("size") && (mesh["physics_mesh"]["size"].asInteger() > 0)) { - LLSD &mesh = iter->second; - if (mesh.has("physics_mesh") && mesh["physics_mesh"].has("size") && (mesh["physics_mesh"]["size"].asInteger() > 0)) - { - return true; - } + return true; } } @@ -4218,9 +4291,9 @@ S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) { LLMutexLock lock(mThread->mHeaderMutex); LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); - if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0) + if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) { - LLSD& header = iter->second; + const LLSD& header = iter->second.second; if (header.has("404")) { @@ -4324,9 +4397,9 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* by { LLMutexLock lock(mThread->mHeaderMutex); LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); - if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0) + if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) { - result = getStreamingCostLegacy(iter->second, radius, bytes, bytes_visible, lod, unscaled_value); + result = getStreamingCostLegacy(iter->second.second, radius, bytes, bytes_visible, lod, unscaled_value); } } if (result > 0.f) @@ -4639,9 +4712,9 @@ bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data) { LLMutexLock lock(mThread->mHeaderMutex); LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); - if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0) + if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) { - LLSD& header = iter->second; + LLSD& header = iter->second.second; bool header_invalid = (header.has("404") || !header.has("lowest_lod") diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index f61da3e571..e3688ff243 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -210,10 +210,8 @@ public: LLCondition* mSignal; //map of known mesh headers - typedef std::map<LLUUID, LLSD> mesh_header_map; + typedef boost::unordered_map<LLUUID, std::pair<U32, LLSD>> mesh_header_map; // pair is header_size and data mesh_header_map mMeshHeader; - - std::map<LLUUID, U32> mMeshHeaderSize; class HeaderRequest : public RequestStats { @@ -283,10 +281,13 @@ public: }; //set of requested skin info - std::set<UUIDBasedRequest> mSkinRequests; + std::deque<UUIDBasedRequest> mSkinRequests; // list of completed skin info requests - std::list<LLMeshSkinInfo> mSkinInfoQ; + std::deque<LLMeshSkinInfo*> mSkinInfoQ; + + // list of skin info requests that have failed or are unavailaibe + std::deque<UUIDBasedRequest> mSkinUnavailableQ; //set of requested decompositions std::set<UUIDBasedRequest> mDecompositionRequests; @@ -304,13 +305,13 @@ public: std::queue<LODRequest> mLODReqQ; //queue of unavailable LODs (either asset doesn't exist or asset doesn't have desired LOD) - std::queue<LODRequest> mUnavailableQ; + std::deque<LODRequest> mUnavailableQ; //queue of successfully loaded meshes - std::queue<LoadedMesh> mLoadedQ; + std::deque<LoadedMesh> mLoadedQ; //map of pending header requests and currently desired LODs - typedef std::map<LLVolumeParams, std::vector<S32> > pending_lod_map; + typedef boost::unordered_map<LLUUID, std::vector<S32> > pending_lod_map; pending_lod_map mPendingLOD; // llcorehttp library interface objects. @@ -354,7 +355,7 @@ public: //send request for skin info, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) - bool fetchMeshSkinInfo(const LLUUID& mesh_id); + bool fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry = true); //send request for decomposition, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) @@ -577,18 +578,20 @@ public: void shutdown(); S32 update(); + void unregisterMesh(LLVOVolume* volume); //mesh management functions S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1); void notifyLoadedMeshes(); void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume); void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod); - void notifySkinInfoReceived(LLMeshSkinInfo& info); + void notifySkinInfoReceived(LLMeshSkinInfo* info); + void notifySkinInfoUnavailable(const LLUUID& info); void notifyDecompositionReceived(LLModel::Decomposition* info); S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); static S32 getActualMeshLOD(LLSD& header, S32 lod); - const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj = nullptr); + const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj = nullptr); LLModel::Decomposition* getDecomposition(const LLUUID& mesh_id); void fetchPhysicsShape(const LLUUID& mesh_id); bool hasPhysicsShape(const LLUUID& mesh_id); @@ -613,10 +616,10 @@ public: static void metricsProgress(unsigned int count); static void metricsUpdate(); - typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map; + typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map; mesh_load_map mLoadingMeshes[4]; - typedef std::unordered_map<LLUUID, LLMeshSkinInfo> skin_map; + typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map; skin_map mSkinMap; typedef std::map<LLUUID, LLModel::Decomposition*> decomposition_map; @@ -627,7 +630,7 @@ public: std::vector<LLMeshRepoThread::LODRequest> mPendingRequests; //list of mesh ids awaiting skin info - typedef std::map<LLUUID, std::set<LLUUID> > skin_load_map; + typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map; skin_load_map mLoadingSkins; //list of mesh ids that need to send skin info fetch requests @@ -652,6 +655,8 @@ public: std::vector<LLMeshUploadThread*> mUploadWaitList; LLPhysicsDecomp* mDecompThread; + + LLFrameTimer mSkinInfoCullTimer; class inventory_data { diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 3401587450..45b42a32ea 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -1905,32 +1905,16 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d if (sloppy_ratio < 0) { // Sloppy method didn't work, try with smaller decimation values - S32 size_vertices = 0; - - for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) - { - const LLVolumeFace &face = base->getVolumeFace(face_idx); - size_vertices += face.mNumVertices; - } - - // Complex models aren't supposed to get here, they are supposed - // to work on a first try of sloppy due to having more viggle room. - // If they didn't, something is likely wrong, no point locking the - // thread in a long calculation that will fail. - const U32 too_many_vertices = 27000; - if (size_vertices > too_many_vertices) - { - LL_WARNS() << "Sloppy optimization method failed for a complex model " << target_model->getName() << LL_ENDL; - } - else { // Find a decimator that does work F32 sloppy_decimation_step = sqrt((F32)decimation); // example: 27->15->9->5->3 F32 sloppy_decimator = indices_decimator / sloppy_decimation_step; + U64Microseconds end_time = LLTimer::getTotalTime() + U64Seconds(5); while (sloppy_ratio < 0 && sloppy_decimator > precise_ratio - && sloppy_decimator > 1)// precise_ratio isn't supposed to be below 1, but check just in case + && sloppy_decimator > 1 // precise_ratio isn't supposed to be below 1, but check just in case + && end_time > LLTimer::getTotalTime()) { sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY); sloppy_decimator = sloppy_decimator / sloppy_decimation_step; diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index ea10aa75ae..0103bf628a 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -1663,7 +1663,7 @@ void LLPanelEditWearable::initPreviousAlphaTextureEntry(LLAvatarAppearanceDefine class LLMetricSystemHandler : public LLCommandHandler { public: - LLMetricSystemHandler() : LLCommandHandler("metricsystem", UNTRUSTED_THROTTLE) { } + LLMetricSystemHandler() : LLCommandHandler("metricsystem", UNTRUSTED_CLICK_ONLY) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 17b4528d97..445bed673b 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -1275,7 +1275,8 @@ LLPanelObjectInventory::LLPanelObjectInventory(const LLPanelObjectInventory::Par mHaveInventory(FALSE), mIsInventoryEmpty(TRUE), mInventoryNeedsUpdate(FALSE), - mInventoryViewModel(p.name) + mInventoryViewModel(p.name), + mShowRootFolder(p.show_root_folder) { // Setup context menu callbacks mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelObjectInventory::doToSelected, this, _2)); @@ -1527,15 +1528,23 @@ void LLPanelObjectInventory::createFolderViews(LLInventoryObject* inventory_root p.font_highlight_color = item_color; LLFolderViewFolder* new_folder = LLUICtrlFactory::create<LLFolderViewFolder>(p); - new_folder->addToFolder(mFolders); - new_folder->toggleOpen(); + + if (mShowRootFolder) + { + new_folder->addToFolder(mFolders); + new_folder->toggleOpen(); + } if (!contents.empty()) { - createViewsForCategory(&contents, inventory_root, new_folder); + createViewsForCategory(&contents, inventory_root, mShowRootFolder ? new_folder : mFolders); } - // Refresh for label to add item count - new_folder->refresh(); + + if (mShowRootFolder) + { + // Refresh for label to add item count + new_folder->refresh(); + } } } diff --git a/indra/newview/llpanelobjectinventory.h b/indra/newview/llpanelobjectinventory.h index 7b9ecfb8f3..0e450d8ce9 100644 --- a/indra/newview/llpanelobjectinventory.h +++ b/indra/newview/llpanelobjectinventory.h @@ -48,8 +48,14 @@ class LLViewerObject; class LLPanelObjectInventory : public LLPanel, public LLVOInventoryListener { public: - // dummy param block for template registration purposes - struct Params : public LLPanel::Params {}; + struct Params : public LLInitParam::Block<Params, LLPanel::Params> + { + Optional<bool> show_root_folder; + + Params() + : show_root_folder("show_root_folder", true) + {} + }; LLPanelObjectInventory(const Params&); virtual ~LLPanelObjectInventory(); @@ -110,6 +116,7 @@ private: BOOL mIsInventoryEmpty; // 'Empty' label BOOL mInventoryNeedsUpdate; // for idle, set on changed callback LLFolderViewModelInventory mInventoryViewModel; + bool mShowRootFolder; }; #endif // LL_LLPANELOBJECTINVENTORY_H diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index f4eaa78f11..deebf0cd1b 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -484,6 +484,30 @@ public: // requires trusted browser to trigger LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { } + virtual bool canHandleUntrusted( + const LLSD& params, + const LLSD& query_map, + LLMediaCtrl* web, + const std::string& nav_type) + { + if (params.size() < 2) + { + return true; // don't block, will fail later + } + + if (nav_type == NAV_TYPE_CLICKED) + { + return true; + } + + const std::string verb = params[1].asString(); + if (verb == "about" || verb == "inspect" || verb == "reportAbuse") + { + return true; + } + return false; + } + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { @@ -1280,6 +1304,8 @@ void LLPanelProfileSecondLife::fillRightsData() void LLPanelProfileSecondLife::fillAgeData(const LLDate &born_on) { + // Date from server comes already converted to stl timezone, + // so display it as an UTC + 0 std::string name_and_date = getString("date_format"); LLSD args_name; args_name["datetime"] = (S32)born_on.secondsSinceEpoch(); diff --git a/indra/newview/llpanelprofileclassifieds.cpp b/indra/newview/llpanelprofileclassifieds.cpp index a3913ddc49..1ff12b4f37 100644 --- a/indra/newview/llpanelprofileclassifieds.cpp +++ b/indra/newview/llpanelprofileclassifieds.cpp @@ -81,6 +81,30 @@ public: std::set<LLUUID> mClassifiedIds; std::string mRequestVerb; + + virtual bool canHandleUntrusted( + const LLSD& params, + const LLSD& query_map, + LLMediaCtrl* web, + const std::string& nav_type) + { + if (params.size() < 1) + { + return true; // don't block, will fail later + } + + if (nav_type == NAV_TYPE_CLICKED) + { + return true; + } + + const std::string verb = params[0].asString(); + if (verb == "create") + { + return false; + } + return true; + } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp index 774119f169..45d0252e4f 100644 --- a/indra/newview/llpanelprofilepicks.cpp +++ b/indra/newview/llpanelprofilepicks.cpp @@ -63,6 +63,30 @@ public: // requires trusted browser to trigger LLPickHandler() : LLCommandHandler("pick", UNTRUSTED_THROTTLE) { } + virtual bool canHandleUntrusted( + const LLSD& params, + const LLSD& query_map, + LLMediaCtrl* web, + const std::string& nav_type) + { + if (params.size() < 1) + { + return true; // don't block, will fail later + } + + if (nav_type == NAV_TYPE_CLICKED) + { + return true; + } + + const std::string verb = params[0].asString(); + if (verb == "create") + { + return false; + } + return true; + } + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 06a6c5e373..b4382a4646 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -173,11 +173,103 @@ class LLFloaterOpenHandler : public LLCommandHandler { public: - // requires trusted browser to trigger + // requires trusted browser to trigger or an explicit click LLFloaterOpenHandler() : LLCommandHandler("openfloater", UNTRUSTED_THROTTLE) { } - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) + bool canHandleUntrusted( + const LLSD& params, + const LLSD& query_map, + LLMediaCtrl* web, + const std::string& nav_type) override + { + if (params.size() != 1) + { + return true; // will fail silently + } + + std::string fl_name = params[0].asString(); + + if (nav_type == NAV_TYPE_CLICKED) + { + const std::list<std::string> blacklist_clicked = { + "camera_presets", + "delete_pref_preset", + "forget_username", + "god_tools", + "group_picker", + "hud", + "incoming_call", + "linkreplace", + "message_critical", // Modal!!! Login specific. + "message_tos", // Modal!!! Login specific. + "save_pref_preset", + "save_camera_preset", + "region_restarting", + "outfit_snapshot", + "upload_anim_bvh", + "upload_anim_anim", + "upload_image", + "upload_model", + "upload_script", + "upload_sound" + }; + return std::find(blacklist_clicked.begin(), blacklist_clicked.end(), fl_name) == blacklist_clicked.end(); + } + else + { + const std::list<std::string> blacklist_untrusted = { + "360capture", + "block_timers", + "add_payment_method", + "appearance", + "associate_listing", + "avatar_picker", + "camera", + "camera_presets", + "classified", + "add_landmark", + "delete_pref_preset", + "env_fixed_environmentent_water", + "env_fixed_environmentent_sky", + "env_edit_extdaycycle", + "font_test", + "forget_username", + "god_tools", + "group_picker", + "hud", + "incoming_call", + "linkreplace", + "mem_leaking", + "marketplace_validation", + "message_critical", // Modal!!! Login specific. If this is in use elsewhere, better to create a non modal variant + "message_tos", // Modal!!! Login specific. + "mute_object_by_name", + "publish_classified", + "save_pref_preset", + "save_camera_preset", + "region_restarting", + "script_debug", + "script_debug_output", + "sell_land", + "outfit_snapshot", + "upload_anim_bvh", + "upload_anim_anim", + "upload_image", + "upload_model", + "upload_script", + "upload_sound" + }; + return std::find(blacklist_untrusted.begin(), blacklist_untrusted.end(), fl_name) == blacklist_untrusted.end(); + } + + + return true; + } + + bool handle( + const LLSD& params, + const LLSD& query_map, + LLMediaCtrl* web) override { if (params.size() != 1) { diff --git a/indra/newview/llviewerinput.cpp b/indra/newview/llviewerinput.cpp index 43b9cd90bd..6bab2c2100 100644 --- a/indra/newview/llviewerinput.cpp +++ b/indra/newview/llviewerinput.cpp @@ -1614,12 +1614,22 @@ BOOL LLViewerInput::handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask, clicktype = CLICK_DOUBLELEFT; } + // If the first LMB click is handled by the menu, skip the following double click + static bool skip_double_click = false; + if (clicktype == CLICK_LEFT && down ) + { + skip_double_click = handled; + } if (double_click_sp && down) { // Consume click. // Due to handling, double click that is not handled will be immediately followed by LMB click } + else if (clicktype == CLICK_DOUBLELEFT && skip_double_click) + { + handled = true; + } // If UI handled 'down', it should handle 'up' as well // If we handle 'down' not by UI, then we should handle 'up'/'level' regardless of UI else if (handled) diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 55ac817479..50252556de 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -232,7 +232,7 @@ class LLInventoryHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLInventoryHandler() : LLCommandHandler("inventory", UNTRUSTED_THROTTLE) { } + LLInventoryHandler() : LLCommandHandler("inventory", UNTRUSTED_CLICK_ONLY) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index f76031953d..6c06360636 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -5613,6 +5613,7 @@ class LLToolsSelectNextPartFace : public view_listener_t } } LLSelectMgr::getInstance()->selectObjectOnly(to_select, new_te); + LLSelectMgr::getInstance()->addAsIndividual(to_select, new_te, false); } else { diff --git a/indra/newview/llviewerpartsource.cpp b/indra/newview/llviewerpartsource.cpp index f042040e98..1751ee1ebb 100644 --- a/indra/newview/llviewerpartsource.cpp +++ b/indra/newview/llviewerpartsource.cpp @@ -793,7 +793,7 @@ void LLViewerPartSourceBeam::update(const F32 dt) } LLViewerPart* part = new LLViewerPart(); - part->init(this, mImagep, NULL); + part->init(this, mImagep, updatePart); part->mFlags = LLPartData::LL_PART_INTERP_COLOR_MASK | LLPartData::LL_PART_INTERP_SCALE_MASK | diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index f4a938e57d..8cfa83ba46 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -228,6 +228,9 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re mColorChanged = FALSE; mSpotLightPriority = 0.f; + mSkinInfoFailed = false; + mSkinInfo = NULL; + mMediaImplList.resize(getNumTEs()); mLastFetchedMediaVersion = -1; mServerDrawableUpdateCount = 0; @@ -244,6 +247,8 @@ LLVOVolume::~LLVOVolume() delete mVolumeImpl; mVolumeImpl = NULL; + gMeshRepo.unregisterMesh(this); + if(!mMediaImplList.empty()) { for(U32 i = 0 ; i < mMediaImplList.size() ; i++) @@ -1095,6 +1100,12 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bo // if it's a mesh if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) { + if (mSkinInfo && mSkinInfo->mMeshID != volume_params.getSculptID()) + { + mSkinInfo = NULL; + mSkinInfoFailed = false; + } + if (!getVolume()->isMeshAssetLoaded()) { //load request not yet issued, request pipeline load this mesh @@ -1106,6 +1117,14 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bo } } + if (!mSkinInfo && !mSkinInfoFailed) + { + const LLMeshSkinInfo* skin_info = gMeshRepo.getSkinInfo(volume_params.getSculptID(), this); + if (skin_info) + { + notifySkinInfoLoaded(skin_info); + } + } } else // otherwise is sculptie { @@ -1158,6 +1177,9 @@ void LLVOVolume::updateSculptTexture() { mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); } + + mSkinInfoFailed = false; + mSkinInfo = NULL; } else { @@ -1212,6 +1234,20 @@ void LLVOVolume::notifyMeshLoaded() updateVisualComplexity(); } +void LLVOVolume::notifySkinInfoLoaded(const LLMeshSkinInfo* skin) +{ + mSkinInfoFailed = false; + mSkinInfo = skin; + + notifyMeshLoaded(); +} + +void LLVOVolume::notifySkinInfoUnavailable() +{ + mSkinInfoFailed = true; + mSkinInfo = nullptr; +} + // sculpt replaces generate() for sculpted surfaces void LLVOVolume::sculpt() { @@ -3645,7 +3681,7 @@ const LLMeshSkinInfo* LLVOVolume::getSkinInfo() const { if (getVolume()) { - return gMeshRepo.getSkinInfo(getMeshID(), this); + return mSkinInfo; } else { diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 4136c13315..12681e2bd9 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -350,6 +350,8 @@ public: void updateVisualComplexity(); void notifyMeshLoaded(); + void notifySkinInfoLoaded(const LLMeshSkinInfo* skin); + void notifySkinInfoUnavailable(); // Returns 'true' iff the media data for this object is in flight bool isMediaDataBeingFetched() const; @@ -433,6 +435,8 @@ private: LLPointer<LLRiggedVolume> mRiggedVolume; + bool mSkinInfoFailed; + LLConstPointer<LLMeshSkinInfo> mSkinInfo; // statics public: static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop diff --git a/indra/newview/skins/default/xui/en/floater_object_weights.xml b/indra/newview/skins/default/xui/en/floater_object_weights.xml index eb283a1043..889efa061c 100644 --- a/indra/newview/skins/default/xui/en/floater_object_weights.xml +++ b/indra/newview/skins/default/xui/en/floater_object_weights.xml @@ -2,7 +2,7 @@ <floater can_close="true" can_tear_off="false" - height="315" + height="289" help_topic="object_weights" layout="topleft" name="object_weights" @@ -320,23 +320,4 @@ top_delta="0" value="Total capacity" width="130" /> - <view_border - bevel_style="none" - follows="top|left" - height="0" - layout="topleft" - left="10" - name="land_impacts_text_border" - top_pad="5" - width="180"/> - - <text - follows="left|top" - height="16" - layout="topleft" - left="10" - name="help_SLURL" - top_pad="10" - value="[secondlife:///app/help/object_weights What is all this?...]" - width="180" /> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_openobject.xml b/indra/newview/skins/default/xui/en/floater_openobject.xml index 912db80bcc..ec03d7d32c 100644 --- a/indra/newview/skins/default/xui/en/floater_openobject.xml +++ b/indra/newview/skins/default/xui/en/floater_openobject.xml @@ -3,7 +3,7 @@ legacy_header_height="18" can_resize="true" default_tab_group="1" - height="370" + height="350" layout="topleft" min_height="190" min_width="285" @@ -31,62 +31,18 @@ background_visible="false" draw_border="false" follows="all" - height="240" + height="265" layout="topleft" + show_root_folder="false" left="10" name="object_contents" top_pad="0" width="284" /> - <view_border - bevel_style="none" - follows="bottom|left" - height="50" - highlight_light_color="0.6 0.6 0.6" - layout="topleft" - left="10" - name="border" - top_pad="5" - width="270"/> - <text - follows="bottom|left" - height="15" - layout="topleft" - left="15" - name="border_note" - text_color="White" - top_delta="5"> - Copy to inventory and wear - </text> - <button - follows="bottom|left" - height="23" - label="Add to outfit" - label_selected="Add to outfit" - layout="topleft" - left="15" - name="copy_and_wear_button" - top_pad="3" - width="135"> - <button.commit_callback - function="OpenObject.MoveAndWear" /> - </button> - <button - follows="bottom|left" - height="23" - label="Replace outfit" - label_selected="Replace outfit" - layout="topleft" - left_pad="5" - name="copy_and_replace_button" - width="120"> - <button.commit_callback - function="OpenObject.ReplaceOutfit" /> - </button> <button follows="bottom|left" height="23" label="Only copy to inventory" - label_selected="Only copy to inventory" + label_selected="Copy to inventory" layout="topleft" left="15" name="copy_to_inventory_button" diff --git a/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml index 41384a77b8..59117c0178 100644 --- a/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml +++ b/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml @@ -82,17 +82,20 @@ width="62"> Name </text> - <line_editor - border_style="line" - border_thickness="1" - follows="left|top" - height="20" - layout="topleft" - left_pad="0" - top_pad="-18" - max_length_chars="255" - name="filter_by_name" - width="161" /> + + <search_editor + follows="left|top" + search_button_visible="false" + height="20" + text_readonly_color="DkGray" + label="Objects by Name" + layout="topleft" + left_pad="0" + top_pad="-18" + name="filter_by_name" + select_on_focus="true" + width="161"> + </search_editor> <text name="linksets_desc_label" height="13" @@ -108,17 +111,19 @@ width="88"> Description </text> - <line_editor - border_style="line" - border_thickness="1" - follows="left|top" - height="20" - layout="topleft" - left_pad="0" - top_pad="-17" - max_length_chars="255" - name="filter_by_description" - width="162" /> + <search_editor + follows="left|top" + search_button_visible="false" + height="20" + text_readonly_color="DkGray" + label="Objects by Description" + layout="topleft" + left_pad="0" + top_pad="-17" + name="filter_by_description" + select_on_focus="true" + width="162"> + </search_editor> <combo_box height="20" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml index 551b477876..777b37d666 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml @@ -9,9 +9,13 @@ follows="all" layout="topleft" > + <!-- + Date from server comes already converted to stl timezone, + so display it as an UTC+0 + --> <string name="date_format" - value="SL birthdate: [mth,datetime,slt] [day,datetime,slt], [year,datetime,slt]" /> + value="SL birthdate: [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc]" /> <string name="age_format" value="[AGE]" /> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 6f95e282ca..b88a01f035 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3983,7 +3983,7 @@ Please check http://status.secondlifegrid.net to see if there is a known problem <string name="Premium_PlusMembership">Premium Plus</string> <string name="InternalMembership">Internal</string> <!-- No need to translate --> - <string name="MembershipUpgradeText">Upgrade to Premium</string> + <string name="MembershipUpgradeText">Change membership plan...</string> <string name="MembershipPremiumText">My Premium membership</string> <!-- Question strings for delete items notifications --> diff --git a/indra/newview/skins/default/xui/en/widgets/filter_editor.xml b/indra/newview/skins/default/xui/en/widgets/filter_editor.xml index 1c4822b8d5..9c80deeafc 100644 --- a/indra/newview/skins/default/xui/en/widgets/filter_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/filter_editor.xml @@ -6,7 +6,7 @@ text_pad_left="7" select_on_focus="true" text_tentative_color="TextFgTentativeColor" - highlight_text_field="false" + highlight_text_field="true" background_image="TextField_Search_Off" background_image_disabled="TextField_Search_Disabled" background_image_focused="TextField_Search_Active" diff --git a/indra/newview/skins/default/xui/en/widgets/search_editor.xml b/indra/newview/skins/default/xui/en/widgets/search_editor.xml index dc5a07bf4f..18d99f1ed1 100644 --- a/indra/newview/skins/default/xui/en/widgets/search_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/search_editor.xml @@ -7,7 +7,7 @@ text_pad_right="6" select_on_focus="true" text_tentative_color="TextFgTentativeColor" - highlight_text_field="false" + highlight_text_field="true" background_image="TextField_Search_Off" background_image_disabled="TextField_Search_Disabled" background_image_focused="TextField_Search_Active" diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index de5ac5ed3d..a1d2d12779 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -518,7 +518,7 @@ class WindowsManifest(ViewerManifest): self.path("alut.dll") # For textures - self.path("openjpeg.dll") + self.path("openjp2.dll") # Uriparser self.path("uriparser.dll") @@ -1498,7 +1498,7 @@ class Linux_i686_Manifest(LinuxManifest): self.path("libdirectfb-1.*.so.*") self.path("libfusion-1.*.so.*") self.path("libdirect-1.*.so.*") - self.path("libopenjpeg.so*") + self.path("libopenjp2.so*") self.path("libdirectfb-1.4.so.5") self.path("libfusion-1.4.so.5") self.path("libdirect-1.4.so.5*") |