# Conflicts:
#	autobuild.xml
#	indra/llcommon/CMakeLists.txt
#	indra/llrender/llgl.cpp
#	indra/newview/llappviewer.cpp
#	indra/newview/llface.cpp
#	indra/newview/llflexibleobject.cpp
#	indra/newview/llvovolume.cpp
master
Ansariel 2022-05-27 11:41:45 +02:00
commit 6ccdf5545d
491 changed files with 18002 additions and 21544 deletions

1
.gitignore vendored
View File

@ -24,6 +24,7 @@ build-vc100/
build-vc120/
build-vc[0-9]*-32*/
build-vc[0-9]*-64*/
build-vc160-64/
indra/CMakeFiles
indra/build-vc[0-9]*
indra/lib/mono/1.0/*.dll

View File

@ -391,35 +391,69 @@
<key>version</key>
<string>1.2.15</string>
</map>
<key>Tracy</key>
<key>tracy</key>
<map>
<key>canonical_repo</key>
<string>https://bitbucket.org/lindenlab/3p-tracy</string>
<key>copyright</key>
<string>Copyright (c) 2017-2021, Bartosz Taudul</string>
<string>Copyright (c) 2017-2021, Bartosz Taudul (wolf@nereid.pl)</string>
<key>description</key>
<string>A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications.</string>
<string>Tracy Profiler Library</string>
<key>license</key>
<string>BSD</string>
<string>bsd</string>
<key>license_file</key>
<string>LICENSES/Tracy.txt</string>
<string>LICENSES/tracy_license.txt</string>
<key>name</key>
<string>Tracy</string>
<string>tracy</string>
<key>platforms</key>
<map>
<key>common</key>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>b0a0fc6b7bda02fe14cccea2dd342058</string>
<string>da7317e4a81609f624f84780f28b07de</string>
<key>url</key>
<string>http://3p.firestormviewer.org/Tracy-0.7.6-windows-210902110.tar.bz2</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/86972/801630/tracy-v0.7.8.563351-darwin64-563351.tar.bz2</string>
</map>
<key>name</key>
<string>common</string>
<string>darwin64</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>47c696cd2966c5cc3c8ba6115dd1f886</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/86973/801641/tracy-v0.7.8.563351-windows-563351.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>b649ee6591e67d2341e886b3fc3484a7</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/86974/801642/tracy-v0.7.8.563351-windows64-563351.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>source</key>
<string>https://bitbucket.org/lindenlab/3p-tracy</string>
<key>source_type</key>
<string>git</string>
<key>version</key>
<string>0.7.6</string>
<string>v0.7.8.563351</string>
</map>
<key>apr_suite</key>
<map>
@ -1220,9 +1254,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>650e836255b6c2ecb93d3f1f7220051c</string>
<string>dce3f3c01fddb400cb143c3283fe9259</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55011/511905/glh_linear-0.0.0-common-538981.tar.bz2</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/82754/775367/glh_linear-0.0.0-common-560278.tar.bz2</string>
</map>
<key>name</key>
<string>common</string>
@ -1231,72 +1265,6 @@
<key>version</key>
<string>0.0.0</string>
</map>
<key>glod</key>
<map>
<key>copyright</key>
<string>Copyright 2003 Jonathan Cohen, Nat Duca, David Luebke, Brenden Schubert - Johns Hopkins University and University of Virginia</string>
<key>license</key>
<string>GLOD Open-Source License Version 1.0</string>
<key>license_file</key>
<string>LICENSES/GLOD.txt</string>
<key>name</key>
<string>glod</string>
<key>platforms</key>
<map>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>94fc457c46e1fb94b31251bd4747d10f</string>
<key>url</key>
<string>http://3p.firestormviewer.org/glod-1.0pre3.171101143-darwin64-171101143.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
<key>linux64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>acc1181cd31ef32c3724eda84ae4b580</string>
<key>url</key>
<string>http://3p.firestormviewer.org/glod-1.0pre3.180990827-linux64-180990827.tar.bz2</string>
</map>
<key>name</key>
<string>linux64</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>e1f8da12a2b7a6c31830b4bb86d31ed6</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
<string>http://3p.firestormviewer.org/glod-1.0pre3.vs2017-1906061512-windows-vs2017-1906061512.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>e906cf08bfbfbd9d4fc78557e021e7d0</string>
<key>url</key>
<string>http://3p.firestormviewer.org/glod-1.0pre3.vs2017-1906061512-windows64-vs2017-1906061512.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>version</key>
<string>1.0pre3.532346</string>
</map>
<key>googlemock</key>
<map>
<key>copyright</key>
@ -2256,6 +2224,62 @@
<key>version</key>
<string>7.11.1.297294</string>
</map>
<key>meshoptimizer</key>
<map>
<key>canonical_repo</key>
<string>https://bitbucket.org/lindenlab/3p-meshoptimizer</string>
<key>copyright</key>
<string>Copyright (c) 2016-2021 Arseny Kapoulkine</string>
<key>description</key>
<string>Meshoptimizer. Mesh optimization library.</string>
<key>license</key>
<string>meshoptimizer</string>
<key>license_file</key>
<string>LICENSES/meshoptimizer.txt</string>
<key>name</key>
<string>meshoptimizer</string>
<key>platforms</key>
<map>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>30bc37db57bbd87c4b5f62634964242a</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/84218/784918/meshoptimizer-0.16.561408-darwin64-561408.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>ca3684bcf0447746cd2844e94f6d1fc7</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/84219/784924/meshoptimizer-0.16.561408-windows-561408.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>aef28c089d20f69d13c9c3e113fb3895</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/84220/784931/meshoptimizer-0.16.561408-windows64-561408.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>version</key>
<string>0.16.561408</string>
</map>
<key>minizip-ng</key>
<map>
<key>canonical_repo</key>
@ -2903,6 +2927,70 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>version</key>
<string>0.132.2</string>
</map>
<key>tracy</key>
<map>
<key>canonical_repo</key>
<string>https://bitbucket.org/lindenlab/3p-tracy</string>
<key>copyright</key>
<string>Copyright (c) 2017-2021, Bartosz Taudul (wolf@nereid.pl)</string>
<key>description</key>
<string>Tracy Profiler Library</string>
<key>license</key>
<string>bsd</string>
<key>license_file</key>
<string>LICENSES/tracy_license.txt</string>
<key>name</key>
<string>tracy</string>
<key>platforms</key>
<map>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>da7317e4a81609f624f84780f28b07de</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/86972/801630/tracy-v0.7.8.563351-darwin64-563351.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>47c696cd2966c5cc3c8ba6115dd1f886</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/86973/801641/tracy-v0.7.8.563351-windows-563351.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>b649ee6591e67d2341e886b3fc3484a7</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/86974/801642/tracy-v0.7.8.563351-windows64-563351.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>source</key>
<string>https://bitbucket.org/lindenlab/3p-tracy</string>
<key>source_type</key>
<string>git</string>
<key>version</key>
<string>v0.7.8.563351</string>
</map>
<key>tut</key>
<map>
<key>copyright</key>

View File

@ -155,7 +155,11 @@ pre_build()
fi
set -x
"$autobuild" configure --quiet -c $variant -- \
# honor autobuild_configure_parameters same as sling-buildscripts
eval_autobuild_configure_parameters=$(eval $(echo echo $autobuild_configure_parameters))
"$autobuild" configure --quiet -c $variant \
${eval_autobuild_configure_parameters:---} \
-DPACKAGE:BOOL=ON \
-DHAVOK:BOOL="$HAVOK" \
-DRELEASE_CRASH_REPORTING:BOOL="$RELEASE_CRASH_REPORTING" \
@ -205,7 +209,11 @@ build()
if $build_viewer
then
begin_section "autobuild $variant"
"$autobuild" build --no-configure -c $variant || fatal "failed building $variant"
# honor autobuild_build_parameters same as sling-buildscripts
eval_autobuild_build_parameters=$(eval $(echo echo $autobuild_build_parameters))
"$autobuild" build --no-configure -c $variant \
$eval_autobuild_build_parameters \
|| fatal "failed building $variant"
echo true >"$build_dir"/build_ok
end_section "autobuild $variant"

View File

@ -280,7 +280,9 @@ Beq Janus
SL-14766
SL-14927
SL-11300
SL-15709
SL-16021
SL-16027
Beth Walcher
Bezilon Kasei
Biancaluce Robbiani
@ -1378,7 +1380,7 @@ Sovereign Engineer
MAINT-7343
SL-11079
OPEN-343
SL-11625
SL-11625
BUG-229030
SL-14705
SL-14706
@ -1386,6 +1388,7 @@ Sovereign Engineer
SL-14731
SL-14732
SL-15096
SL-16127
SpacedOut Frye
VWR-34
VWR-45

View File

@ -77,12 +77,12 @@ endif (USE_AVX_OPTIMIZATION)
add_subdirectory(cmake)
# <FS:Beq> Tracy Profiler support
option(USE_TRACY_PROFILER "Tracy Profiler support" OFF)
if (USE_TRACY_PROFILER)
option(USE_TRACY "Tracy Profiler support" OFF)
if (USE_TRACY)
message(STATUS "Compiling with Tracy profiler")
else (USE_TRACY_PROFILER)
else (USE_TRACY)
message(STATUS "Compiling without Tracy profiler")
endif (USE_TRACY_PROFILER)
endif (USE_TRACY)
# </FS:Beq> Tracy Profiler support
add_subdirectory(${LIBS_OPEN_PREFIX}llaudio)
@ -95,6 +95,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llkdu)
add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj)
add_subdirectory(${LIBS_OPEN_PREFIX}llinventory)
add_subdirectory(${LIBS_OPEN_PREFIX}llmath)
add_subdirectory(${LIBS_OPEN_PREFIX}llmeshoptimizer)
add_subdirectory(${LIBS_OPEN_PREFIX}llmessage)
add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive)
add_subdirectory(${LIBS_OPEN_PREFIX}llrender)

View File

@ -49,8 +49,8 @@ if(NON_RELEASE_CRASH_REPORTING)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DLL_SEND_CRASH_REPORTS=1")
endif()
# Don't bother with a MinSizeRel build.
set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;Debug" CACHE STRING
# Don't bother with MinSizeRel or Debug builds.
set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release" CACHE STRING
"Supported build types." FORCE)
@ -87,12 +87,18 @@ if (WINDOWS)
#if( ADDRESS_SIZE EQUAL 32 )
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /p:PreferredToolArchitecture=x64")
#endif()
# Preserve first-pass-through versions (ie no FORCE overwrite). Prevents recursive addition of /Zo (04/2021)
set(OG_CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE} CACHE STRING "OG_CXX_FLAGS_RELEASE")
set(OG_CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO} CACHE STRING "OG_CXX_FLAGS_RELWITHDEBINFO")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
"${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Zo"
"${OG_CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Zo"
CACHE STRING "C++ compiler release-with-debug options" FORCE)
set(CMAKE_CXX_FLAGS_RELEASE
"${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /Zo"
"${OG_CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /Zo"
CACHE STRING "C++ compiler release options" FORCE)
# zlib has assembly-language object files incompatible with SAFESEH
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE /SAFESEH:NO /NODEFAULTLIB:LIBCMT /IGNORE:4099")

View File

@ -40,7 +40,6 @@ set(cmake_SOURCE_FILES
FreeType.cmake
GLEXT.cmake
GLH.cmake
GLOD.cmake
## GStreamer010Plugin.cmake
GoogleMock.cmake
Growl.cmake
@ -60,6 +59,7 @@ set(cmake_SOURCE_FILES
LLKDU.cmake
LLLogin.cmake
LLMath.cmake
LLMeshOptimizer.cmake
LLMessage.cmake
LLPhysicsExtensions.cmake
LLPlugin.cmake
@ -73,6 +73,7 @@ set(cmake_SOURCE_FILES
LLXML.cmake
Linking.cmake
MediaPluginBase.cmake
MESHOPTIMIZER.cmake
NDOF.cmake
OPENAL.cmake
OpenGL.cmake

View File

@ -57,7 +57,6 @@ if(WINDOWS)
libaprutil-1.dll
libapriconv-1.dll
nghttp2.dll
glod.dll
libhunspell.dll
uriparser.dll
)
@ -151,6 +150,7 @@ if(WINDOWS)
msvcp${MSVC_VER}.dll
#msvcr${MSVC_VER}.dll # <FS:Ansariel> Can't build with older VS versions anyway - no need trying to copy this file
vcruntime${MSVC_VER}.dll
vcruntime${MSVC_VER}_1.dll
)
# <FS:Ansariel> Try using the VC runtime redistributables that came with the VS installation first
if(redist_path AND EXISTS "${redist_path}/${release_msvc_file}")
@ -198,7 +198,6 @@ elseif(DARWIN)
libaprutil-1.0.dylib
libaprutil-1.dylib
${EXPAT_COPY}
libGLOD.dylib
libhunspell-1.3.0.dylib
libndofdev.dylib
libnghttp2.dylib
@ -247,7 +246,6 @@ elseif(LINUX)
set(release_files
#libdb-5.1.so
${EXPAT_COPY}
#libGLOD.so
libhunspell-1.3.so.0.0.0
libopenal.so
#libopenjpeg.so

View File

@ -1,15 +0,0 @@
# -*- cmake -*-
#if (USESYSTEMLIBS)
# set(GLOD_FIND_REQUIRED true)
# include(FindGLOD)
#else (USESYSTEMLIBS)
include(Prebuilt)
use_prebuilt_binary(glod)
set(GLOD_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
if(LINUX)
set(GLOD_LIBRARIES GLOD vds)
else()
set(GLOD_LIBRARIES GLOD)
endif()
#endif (USESYSTEMLIBS)

View File

@ -39,7 +39,8 @@ else (LINUX)
${BOOST_FIBER_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_THREAD_LIBRARY}
${BOOST_SYSTEM_LIBRARY} )
${BOOST_SYSTEM_LIBRARY}
)
endif (LINUX)
set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a static library.")

View File

@ -0,0 +1,7 @@
# -*- cmake -*-
set(LLMESHOPTIMIZER_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llmeshoptimizer
)
set(LLMESHOPTIMIZER_LIBRARIES llmeshoptimizer)

View File

@ -0,0 +1,16 @@
# -*- cmake -*-
include(Linking)
include(Prebuilt)
use_prebuilt_binary(meshoptimizer)
if (WINDOWS)
set(MESHOPTIMIZER_LIBRARIES meshoptimizer.lib)
elseif (LINUX)
set(MESHOPTIMIZER_LIBRARIES meshoptimizer.o)
elseif (DARWIN)
set(MESHOPTIMIZER_LIBRARIES libmeshoptimizer.a)
endif (WINDOWS)
set(MESHOPTIMIZER_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/meshoptimizer)

View File

@ -1,16 +1,29 @@
# Tracy Profiler support.
if (USE_TRACY_PROFILER)
include(Prebuilt)
use_prebuilt_binary(Tracy)
if (WINDOWS)
# set(TRACY_LIBRARIES
# ${ARCH_PREBUILT_DIRS_RELEASE}/Tracy.lib
add_definitions(-DTRACY_ENABLE=1 -DTRACY_NO_FASTTIMERS -DWINVER=0x0601 )
set(TRACY_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tracy)
elseif (DARWIN)
message(FATAL_ERROR "Tracy is not yet implemented in FS for OSX.")
else (WINDOWS)
add_definitions(-DTRACY_ENABLE=1 -DTRACY_NO_FASTTIMERS )
set(TRACY_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tracy)
endif (WINDOWS)
endif (USE_TRACY_PROFILER)
# -*- cmake -*-
include(Prebuilt)
set(USE_TRACY OFF CACHE BOOL "Use Tracy profiler.")
if (USE_TRACY)
set(TRACY_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tracy)
# See: indra/llcommon/llprofiler.h
add_definitions(-DLL_PROFILER_CONFIGURATION=3)
use_prebuilt_binary(tracy)
# if (WINDOWS)
# MESSAGE(STATUS "Including Tracy for Windows: '${TRACY_INCLUDE_DIR}'")
# endif (WINDOWS)
# if (DARWIN)
# MESSAGE(STATUS "Including Tracy for Darwin: '${TRACY_INCLUDE_DIR}'")
# endif (DARWIN)
# if (LINUX)
# MESSAGE(STATUS "Including Tracy for Linux: '${TRACY_INCLUDE_DIR}'")
# endif (LINUX)
else (USE_TRACY)
# Tracy.cmake should not set LLCOMMON_INCLUDE_DIRS, let LLCommon.cmake do that
set(TRACY_INCLUDE_DIR "")
set(TRACY_LIBRARY "")
endif (USE_TRACY)

View File

@ -1,3 +1,4 @@
euclid 5/29/2020
euclid 7/23/2020
euclid 4/29/2021
euclid 10/5/2021 DRTVWR-546

View File

@ -899,6 +899,49 @@ class LLManifest(object, metaclass=LLManifestRegistry):
# particular, let caller notice 0.
return count
def path_optional(self, src, dst=None):
sys.stdout.flush()
if src == None:
raise ManifestError("No source file, dst is " + dst)
if dst == None:
dst = src
dst = os.path.join(self.get_dst_prefix(), dst)
sys.stdout.write("Processing %s => %s ... " % (src, self._relative_dst_path(dst)))
def try_path(src):
# expand globs
count = 0
if self.wildcard_pattern.search(src):
for s,d in self.expand_globs(src, dst):
assert(s != d)
count += self.process_file(s, d)
else:
# if we're specifying a single path (not a glob),
# we should error out if it doesn't exist
self.check_file_exists(src)
count += self.process_either(src, dst)
return count
try_prefixes = [self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix()]
for pfx in try_prefixes:
try:
count = try_path(os.path.join(pfx, src))
except MissingError:
# if we produce MissingError, just try the next prefix
continue
# If we actually found nonzero files, stop looking
if count:
break
else:
sys.stdout.write("Skipping %s\n" % (src))
return 0
print("%d files" % count)
# Let caller check whether we processed as many files as expected. In
# particular, let caller notice 0.
return count
def do(self, *actions):
self.actions = actions
self.construct()

View File

@ -1620,7 +1620,7 @@ BOOL LLAvatarAppearance::allocateCollisionVolumes( U32 num )
delete_and_clear_array(mCollisionVolumes);
mNumCollisionVolumes = 0;
mCollisionVolumes = new(std::nothrow) LLAvatarJointCollisionVolume[num];
mCollisionVolumes = new LLAvatarJointCollisionVolume[num];
if (!mCollisionVolumes)
{
LL_WARNS() << "Failed to allocate collision volumes" << LL_ENDL;

View File

@ -77,81 +77,71 @@ protected:
//-----------------------------------------------------------------------------
LL_ALIGN_PREFIX(16)
class LLDriverParam : public LLViewerVisualParam
class alignas(16) LLDriverParam : public LLViewerVisualParam
{
LL_ALIGN_NEW
private:
// Hide the default constructor. Force construction with LLAvatarAppearance.
LLDriverParam() {}
// Hide the default constructor. Force construction with LLAvatarAppearance.
LLDriverParam() {}
public:
LLDriverParam(LLAvatarAppearance *appearance, LLWearable* wearable = NULL);
~LLDriverParam();
LLDriverParam(LLAvatarAppearance* appearance, LLWearable* wearable = NULL);
~LLDriverParam();
void* operator new(size_t size)
{
return ll_aligned_malloc_16(size);
}
// Special: These functions are overridden by child classes
LLDriverParamInfo* getInfo() const { return (LLDriverParamInfo*)mInfo; }
// This sets mInfo and calls initialization functions
BOOL setInfo(LLDriverParamInfo* info);
void operator delete(void* ptr)
{
ll_aligned_free_16(ptr);
}
LLAvatarAppearance* getAvatarAppearance() { return mAvatarAppearance; }
const LLAvatarAppearance* getAvatarAppearance() const { return mAvatarAppearance; }
// Special: These functions are overridden by child classes
LLDriverParamInfo* getInfo() const { return (LLDriverParamInfo*)mInfo; }
// This sets mInfo and calls initialization functions
BOOL setInfo(LLDriverParamInfo *info);
void updateCrossDrivenParams(LLWearableType::EType driven_type);
LLAvatarAppearance* getAvatarAppearance() { return mAvatarAppearance; }
const LLAvatarAppearance* getAvatarAppearance() const { return mAvatarAppearance; }
/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const;
void updateCrossDrivenParams(LLWearableType::EType driven_type);
// LLVisualParam Virtual functions
/*virtual*/ void apply(ESex sex) {} // apply is called separately for each driven param.
// <FS:Ansariel> [Legacy Bake]
///*virtual*/ void setWeight(F32 weight);
///*virtual*/ void setAnimationTarget(F32 target_value);
///*virtual*/ void stopAnimating();
/*virtual*/ void setWeight(F32 weight, BOOL upload_bake);
/*virtual*/ void setAnimationTarget(F32 target_value, BOOL upload_bake);
/*virtual*/ void stopAnimating(BOOL upload_bake);
// </FS:Ansariel> [Legacy Bake]
/*virtual*/ BOOL linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params);
/*virtual*/ void resetDrivenParams();
/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const;
// LLViewerVisualParam Virtual functions
/*virtual*/ F32 getTotalDistortion();
/*virtual*/ const LLVector4a& getAvgDistortion();
/*virtual*/ F32 getMaxDistortion();
/*virtual*/ LLVector4a getVertexDistortion(S32 index, LLPolyMesh* poly_mesh);
/*virtual*/ const LLVector4a* getFirstDistortion(U32* index, LLPolyMesh** poly_mesh);
/*virtual*/ const LLVector4a* getNextDistortion(U32* index, LLPolyMesh** poly_mesh);
// LLVisualParam Virtual functions
/*virtual*/ void apply( ESex sex ) {} // apply is called separately for each driven param.
// <FS:Ansariel> [Legacy Bake]
///*virtual*/ void setWeight(F32 weight);
///*virtual*/ void setAnimationTarget( F32 target_value);
///*virtual*/ void stopAnimating();
/*virtual*/ void setWeight(F32 weight, BOOL upload_bake);
/*virtual*/ void setAnimationTarget( F32 target_value, BOOL upload_bake);
/*virtual*/ void stopAnimating(BOOL upload_bake);
// </FS:Ansariel> [Legacy Bake]
/*virtual*/ BOOL linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params);
/*virtual*/ void resetDrivenParams();
// LLViewerVisualParam Virtual functions
/*virtual*/ F32 getTotalDistortion();
/*virtual*/ const LLVector4a& getAvgDistortion();
/*virtual*/ F32 getMaxDistortion();
/*virtual*/ LLVector4a getVertexDistortion(S32 index, LLPolyMesh *poly_mesh);
/*virtual*/ const LLVector4a* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh);
/*virtual*/ const LLVector4a* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh);
S32 getDrivenParamsCount() const;
const LLViewerVisualParam* getDrivenParam(S32 index) const;
S32 getDrivenParamsCount() const;
const LLViewerVisualParam* getDrivenParam(S32 index) const;
typedef std::vector<LLDrivenEntry> entry_list_t;
entry_list_t& getDrivenList() { return mDriven; }
typedef std::vector<LLDrivenEntry> entry_list_t;
entry_list_t& getDrivenList() { return mDriven; }
void setDrivenList(entry_list_t& driven_list) { mDriven = driven_list; }
protected:
LLDriverParam(const LLDriverParam& pOther);
F32 getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight);
// <FS:Ansariel> [Legacy Bake]
//void setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight);
void setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight, bool upload_bake);
// </FS:Ansariel> [Legacy Bake]
LLDriverParam(const LLDriverParam& pOther);
F32 getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight);
// <FS:Ansariel> [Legacy Bake]
//void setDrivenWeight(LLDrivenEntry* driven, F32 driven_weight);
void setDrivenWeight(LLDrivenEntry* driven, F32 driven_weight, bool upload_bake);
// </FS:Ansariel> [Legacy Bake]
LL_ALIGN_16(LLVector4a mDefaultVec); // temp holder
entry_list_t mDriven;
LLViewerVisualParam* mCurrentDistortionParam;
// Backlink only; don't make this an LLPointer.
LLAvatarAppearance* mAvatarAppearance;
LLWearable* mWearablep;
} LL_ALIGN_POSTFIX(16);
LL_ALIGN_16(LLVector4a mDefaultVec); // temp holder
entry_list_t mDriven;
LLViewerVisualParam* mCurrentDistortionParam;
// Backlink only; don't make this an LLPointer.
LLAvatarAppearance* mAvatarAppearance;
LLWearable* mWearablep;
};
#endif // LL_LLDRIVERPARAM_H

View File

@ -541,8 +541,6 @@ F32 LLPolyMorphTarget::getMaxDistortion()
//-----------------------------------------------------------------------------
// apply()
//-----------------------------------------------------------------------------
static LLTrace::BlockTimerStatHandle FTM_APPLY_MORPH_TARGET("Apply Morph");
void LLPolyMorphTarget::apply( ESex avatar_sex )
{
if (!mMorphData || mNumMorphMasksPending > 0)
@ -550,7 +548,7 @@ void LLPolyMorphTarget::apply( ESex avatar_sex )
return;
}
LL_RECORD_BLOCK_TIME(FTM_APPLY_MORPH_TARGET);
LL_PROFILE_ZONE_SCOPED;
mLastSex = avatar_sex;

View File

@ -41,24 +41,14 @@ class LLWearable;
//-----------------------------------------------------------------------------
// LLPolyMorphData()
//-----------------------------------------------------------------------------
LL_ALIGN_PREFIX(16)
class LLPolyMorphData
class alignas(16) LLPolyMorphData
{
LL_ALIGN_NEW
public:
LLPolyMorphData(const std::string& morph_name);
~LLPolyMorphData();
LLPolyMorphData(const LLPolyMorphData &rhs);
void* operator new(size_t size)
{
return ll_aligned_malloc_16(size);
}
void operator delete(void* ptr)
{
ll_aligned_free_16(ptr);
}
BOOL loadBinary(LLFILE* fp, LLPolyMeshSharedData *mesh);
const std::string& getName() { return mName; }
@ -76,7 +66,7 @@ public:
F32 mTotalDistortion; // vertex distortion summed over entire morph
F32 mMaxDistortion; // maximum single vertex distortion in a given morph
LL_ALIGN_16(LLVector4a mAvgDistortion); // average vertex distortion, to infer directionality of the morph
LLVector4a mAvgDistortion; // average vertex distortion, to infer directionality of the morph
LLPolyMeshSharedData* mMesh;
private:
@ -154,8 +144,9 @@ protected:
// These morph targets must be topologically consistent with a given Polymesh
// (share face sets)
//-----------------------------------------------------------------------------
class LLPolyMorphTarget : public LLViewerVisualParam
class alignas(16) LLPolyMorphTarget : public LLViewerVisualParam
{
LL_ALIGN_NEW
public:
LLPolyMorphTarget(LLPolyMesh *poly_mesh);
~LLPolyMorphTarget();
@ -184,16 +175,6 @@ public:
void applyVolumeChanges(F32 delta_weight); // SL-315 - for resetSkeleton()
void* operator new(size_t size)
{
return ll_aligned_malloc_16(size);
}
void operator delete(void* ptr)
{
ll_aligned_free_16(ptr);
}
protected:
LLPolyMorphTarget(const LLPolyMorphTarget& pOther);

View File

@ -190,11 +190,9 @@ BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info)
//-----------------------------------------------------------------------------
// apply()
//-----------------------------------------------------------------------------
static LLTrace::BlockTimerStatHandle FTM_POLYSKELETAL_DISTORTION_APPLY("Skeletal Distortion");
void LLPolySkeletalDistortion::apply( ESex avatar_sex )
{
LL_RECORD_BLOCK_TIME(FTM_POLYSKELETAL_DISTORTION_APPLY);
LL_PROFILE_ZONE_SCOPED;
F32 effective_weight = ( getSex() & avatar_sex ) ? mCurWeight : getDefaultWeight();

View File

@ -62,9 +62,9 @@ struct LLPolySkeletalBoneInfo
BOOL mHasPositionDeformation;
};
LL_ALIGN_PREFIX(16)
class LLPolySkeletalDistortionInfo : public LLViewerVisualParamInfo
class alignas(16) LLPolySkeletalDistortionInfo : public LLViewerVisualParamInfo
{
LL_ALIGN_NEW
friend class LLPolySkeletalDistortion;
public:
@ -73,19 +73,6 @@ public:
/*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
void* operator new(size_t size)
{
return ll_aligned_malloc_16(size);
}
void operator delete(void* ptr)
{
ll_aligned_free_16(ptr);
}
protected:
typedef std::vector<LLPolySkeletalBoneInfo> bone_info_list_t;
bone_info_list_t mBoneInfoList;
@ -95,19 +82,10 @@ protected:
// LLPolySkeletalDeformation
// A set of joint scale data for deforming the avatar mesh
//-----------------------------------------------------------------------------
class LLPolySkeletalDistortion : public LLViewerVisualParam
class alignas(16) LLPolySkeletalDistortion : public LLViewerVisualParam
{
LL_ALIGN_NEW
public:
void* operator new(size_t size)
{
return ll_aligned_malloc_16(size);
}
void operator delete(void* ptr)
{
ll_aligned_free_16(ptr);
}
LLPolySkeletalDistortion(LLAvatarAppearance *avatarp);
~LLPolySkeletalDistortion();

View File

@ -142,17 +142,8 @@ BOOL LLTexLayerSetBuffer::renderTexLayerSet(LLRenderTarget* bound_target)
BOOL success = TRUE;
bool use_shaders = LLGLSLShader::sNoFixedFunction;
if (use_shaders)
{
gAlphaMaskProgram.bind();
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
else
{
gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.00f);
}
gAlphaMaskProgram.bind();
gAlphaMaskProgram.setMinimumAlpha(0.004f);
LLVertexBuffer::unbind();
@ -167,10 +158,7 @@ BOOL LLTexLayerSetBuffer::renderTexLayerSet(LLRenderTarget* bound_target)
midRenderTexLayerSet(success, bound_target);
// </FS:Ansariel> [Legacy Bake]
if (use_shaders)
{
gAlphaMaskProgram.unbind();
}
gAlphaMaskProgram.unbind();
LLVertexBuffer::unbind();
@ -397,8 +385,6 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height, LLRenderTarget*
}
}
bool use_shaders = LLGLSLShader::sNoFixedFunction;
LLGLSUIDefault gls_ui;
LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
gGL.setColorMask(true, true);
@ -407,20 +393,14 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height, LLRenderTarget*
{
gGL.flush();
LLGLDisable no_alpha(GL_ALPHA_TEST);
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.0f);
}
gAlphaMaskProgram.setMinimumAlpha(0.0f);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color4f( 0.f, 0.f, 0.f, 1.f );
gl_rect_2d_simple( width, height );
gGL.flush();
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
if (mIsVisible)
@ -447,10 +427,7 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height, LLRenderTarget*
gGL.setSceneBlendType(LLRender::BT_REPLACE);
LLGLDisable no_alpha(GL_ALPHA_TEST);
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.f);
}
gAlphaMaskProgram.setMinimumAlpha(0.f);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color4f( 0.f, 0.f, 0.f, 0.f );
@ -459,10 +436,7 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height, LLRenderTarget*
gGL.setSceneBlendType(LLRender::BT_ALPHA);
gGL.flush();
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
return success;
@ -501,10 +475,9 @@ const LLTexLayerSetBuffer* LLTexLayerSet::getComposite() const
return mComposite;
}
static LLTrace::BlockTimerStatHandle FTM_GATHER_MORPH_MASK_ALPHA("gatherMorphMaskAlpha");
void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 origin_x, S32 origin_y, S32 width, S32 height, LLRenderTarget* bound_target)
{
LL_RECORD_BLOCK_TIME(FTM_GATHER_MORPH_MASK_ALPHA);
LL_PROFILE_ZONE_SCOPED;
memset(data, 255, width * height);
for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
@ -517,14 +490,11 @@ void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 origin_x, S32 origin_y, S
renderAlphaMaskTextures(origin_x, origin_y, width, height, bound_target, true);
}
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_MASK_TEXTURES("renderAlphaMaskTextures");
void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target, bool forceClear)
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_MASK_TEXTURES);
LL_PROFILE_ZONE_SCOPED;
const LLTexLayerSetInfo *info = getInfo();
bool use_shaders = LLGLSLShader::sNoFixedFunction;
gGL.setColorMask(false, true);
gGL.setSceneBlendType(LLRender::BT_REPLACE);
@ -538,7 +508,6 @@ void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height,
{
LLGLSUIDefault gls_ui;
gGL.getTexUnit(0)->bind(tex);
gGL.getTexUnit(0)->setTextureBlendType( LLTexUnit::TB_REPLACE );
gl_rect_2d_simple_tex( width, height );
}
}
@ -549,20 +518,14 @@ void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height,
// Set the alpha channel to one (clean up after previous blending)
gGL.flush();
LLGLDisable no_alpha(GL_ALPHA_TEST);
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.f);
}
gAlphaMaskProgram.setMinimumAlpha(0.f);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color4f( 0.f, 0.f, 0.f, 1.f );
gl_rect_2d_simple( width, height );
gGL.flush();
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
// (Optional) Mask out part of the baked texture with alpha masks
@ -570,7 +533,6 @@ void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height,
if (mMaskLayerList.size() > 0)
{
gGL.setSceneBlendType(LLRender::BT_MULT_ALPHA);
gGL.getTexUnit(0)->setTextureBlendType( LLTexUnit::TB_REPLACE );
for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++)
{
LLTexLayerInterface* layer = *iter;
@ -583,7 +545,6 @@ void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height,
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
gGL.setColorMask(true, true);
gGL.setSceneBlendType(LLRender::BT_ALPHA);
}
@ -1109,13 +1070,6 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bou
// *TODO: Is this correct?
//gPipeline.disableLights();
stop_glerror();
if (!LLGLSLShader::sNoFixedFunction)
{
glDisable(GL_LIGHTING);
}
stop_glerror();
bool use_shaders = LLGLSLShader::sNoFixedFunction;
LLColor4 net_color;
BOOL color_specified = findNetColor(&net_color);
@ -1202,10 +1156,7 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bou
LLGLDisable alpha_test(no_alpha_test ? GL_ALPHA_TEST : 0);
if (no_alpha_test)
{
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.f);
}
gAlphaMaskProgram.setMinimumAlpha(0.f);
}
LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode();
@ -1219,10 +1170,7 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bou
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
if (no_alpha_test)
{
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
}
}
@ -1256,18 +1204,12 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bou
color_specified )
{
LLGLDisable no_alpha(GL_ALPHA_TEST);
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.000f);
}
gAlphaMaskProgram.setMinimumAlpha(0.000f);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color4fv( net_color.mV );
gl_rect_2d_simple( width, height );
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
if( alpha_mask_specified || getInfo()->mWriteAllChannels )
@ -1355,25 +1297,17 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
gGL.flush();
bool use_shaders = LLGLSLShader::sNoFixedFunction;
if( !getInfo()->mStaticImageFileName.empty() )
{
LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask );
if( tex )
{
LLGLSNoAlphaTest gls_no_alpha_test;
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.f);
}
gAlphaMaskProgram.setMinimumAlpha(0.f);
gGL.getTexUnit(0)->bind(tex, TRUE);
gl_rect_2d_simple_tex( width, height );
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
else
{
@ -1388,18 +1322,11 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
if (tex)
{
LLGLSNoAlphaTest gls_no_alpha_test;
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.f);
}
gAlphaMaskProgram.setMinimumAlpha(0.f);
gGL.getTexUnit(0)->bind(tex);
gl_rect_2d_simple_tex( width, height );
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
success = TRUE;
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
}
}
@ -1412,7 +1339,6 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
addAlphaMask(data, originX, originY, width, height, bound_target);
}
static LLTrace::BlockTimerStatHandle FTM_RENDER_MORPH_MASKS("renderMorphMasks");
void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, LLRenderTarget* bound_target, bool force_render)
{
if (!force_render && !hasMorph())
@ -1420,18 +1346,12 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
LL_DEBUGS() << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
return;
}
LL_RECORD_BLOCK_TIME(FTM_RENDER_MORPH_MASKS);
LL_PROFILE_ZONE_SCOPED;
BOOL success = TRUE;
llassert( !mParamAlphaList.empty() );
bool use_shaders = LLGLSLShader::sNoFixedFunction;
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.f);
}
gAlphaMaskProgram.setMinimumAlpha(0.f);
gGL.setColorMask(false, true);
LLTexLayerParamAlpha* first_param = *mParamAlphaList.begin();
@ -1516,10 +1436,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
gl_rect_2d_simple( width, height );
}
if (use_shaders)
{
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
gAlphaMaskProgram.setMinimumAlpha(0.004f);
LLGLSUIDefault gls_ui;
@ -1678,10 +1595,9 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
}
}
static LLTrace::BlockTimerStatHandle FTM_ADD_ALPHA_MASK("addAlphaMask");
void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height, LLRenderTarget* bound_target)
{
LL_RECORD_BLOCK_TIME(FTM_ADD_ALPHA_MASK);
LL_PROFILE_ZONE_SCOPED;
S32 size = width * height;
const U8* alphaData = getAlphaData();
if (!alphaData && hasAlphaParams())
@ -2022,10 +1938,9 @@ void LLTexLayerStaticImageList::deleteCachedImages()
// Returns an LLImageTGA that contains the encoded data from a tga file named file_name.
// Caches the result to speed identical subsequent requests.
static LLTrace::BlockTimerStatHandle FTM_LOAD_STATIC_TGA("getImageTGA");
LLImageTGA* LLTexLayerStaticImageList::getImageTGA(const std::string& file_name)
{
LL_RECORD_BLOCK_TIME(FTM_LOAD_STATIC_TGA);
LL_PROFILE_ZONE_SCOPED;
const char *namekey = mImageNames.addString(file_name);
image_tga_map_t::const_iterator iter = mStaticImageListTGA.find(namekey);
if( iter != mStaticImageListTGA.end() )
@ -2052,10 +1967,9 @@ LLImageTGA* LLTexLayerStaticImageList::getImageTGA(const std::string& file_name)
// Returns a GL Image (without a backing ImageRaw) that contains the decoded data from a tga file named file_name.
// Caches the result to speed identical subsequent requests.
static LLTrace::BlockTimerStatHandle FTM_LOAD_STATIC_TEXTURE("getTexture");
LLGLTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name, BOOL is_mask)
{
LL_RECORD_BLOCK_TIME(FTM_LOAD_STATIC_TEXTURE);
LL_PROFILE_ZONE_SCOPED;
LLPointer<LLGLTexture> tex;
const char *namekey = mImageNames.addString(file_name);
@ -2102,10 +2016,9 @@ LLGLTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name,
// Reads a .tga file, decodes it, and puts the decoded data in image_raw.
// Returns TRUE if successful.
static LLTrace::BlockTimerStatHandle FTM_LOAD_IMAGE_RAW("loadImageRaw");
BOOL LLTexLayerStaticImageList::loadImageRaw(const std::string& file_name, LLImageRaw* image_raw)
{
LL_RECORD_BLOCK_TIME(FTM_LOAD_IMAGE_RAW);
LL_PROFILE_ZONE_SCOPED;
BOOL success = FALSE;
std::string path;
path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name);

View File

@ -279,10 +279,9 @@ BOOL LLTexLayerParamAlpha::getSkip() const
}
static LLTrace::BlockTimerStatHandle FTM_TEX_LAYER_PARAM_ALPHA("alpha render");
BOOL LLTexLayerParamAlpha::render(S32 x, S32 y, S32 width, S32 height)
{
LL_RECORD_BLOCK_TIME(FTM_TEX_LAYER_PARAM_ALPHA);
LL_PROFILE_ZONE_SCOPED;
BOOL success = TRUE;
if (!mTexLayer)

View File

@ -63,23 +63,14 @@ protected:
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL_ALIGN_PREFIX(16)
class LLTexLayerParamAlpha : public LLTexLayerParam
class alignas(16) LLTexLayerParamAlpha : public LLTexLayerParam
{
LL_ALIGN_NEW
public:
LLTexLayerParamAlpha( LLTexLayerInterface* layer );
LLTexLayerParamAlpha( LLAvatarAppearance* appearance );
/*virtual*/ ~LLTexLayerParamAlpha();
void* operator new(size_t size)
{
return ll_aligned_malloc_16(size);
}
void operator delete(void* ptr)
{
ll_aligned_free_16(ptr);
}
/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const;
// LLVisualParam Virtual functions
@ -157,9 +148,9 @@ private:
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL_ALIGN_PREFIX(16)
class LLTexLayerParamColor : public LLTexLayerParam
class alignas(16) LLTexLayerParamColor : public LLTexLayerParam
{
LL_ALIGN_NEW
public:
enum EColorOperation
{
@ -172,16 +163,6 @@ public:
LLTexLayerParamColor( LLTexLayerInterface* layer );
LLTexLayerParamColor( LLAvatarAppearance* appearance );
void* operator new(size_t size)
{
return ll_aligned_malloc_16(size);
}
void operator delete(void* ptr)
{
ll_aligned_free_16(ptr);
}
/* virtual */ ~LLTexLayerParamColor();
/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const;
@ -216,8 +197,8 @@ protected:
//virtual void onGlobalColorChanged() {}
virtual void onGlobalColorChanged(bool upload_bake) {}
private:
LL_ALIGN_16(LLVector4a mAvgDistortionVec);
} LL_ALIGN_POSTFIX(16);
LLVector4a mAvgDistortionVec;
};
class LLTexLayerParamColorInfo : public LLViewerVisualParamInfo
{

View File

@ -196,20 +196,15 @@ void LLCharacter::requestStopMotion( LLMotion* motion)
//-----------------------------------------------------------------------------
// updateMotions()
//-----------------------------------------------------------------------------
static LLTrace::BlockTimerStatHandle FTM_UPDATE_ANIMATION("Update Animation");
static LLTrace::BlockTimerStatHandle FTM_UPDATE_HIDDEN_ANIMATION("Update Hidden Anim");
static LLTrace::BlockTimerStatHandle FTM_UPDATE_MOTIONS("Update Motions");
void LLCharacter::updateMotions(e_update_t update_type)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
if (update_type == HIDDEN_UPDATE)
{
LL_RECORD_BLOCK_TIME(FTM_UPDATE_HIDDEN_ANIMATION);
mMotionController.updateMotionsMinimal();
}
else
{
LL_RECORD_BLOCK_TIME(FTM_UPDATE_ANIMATION);
// unpause if the number of outstanding pause requests has dropped to the initial one
if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1)
{
@ -217,7 +212,6 @@ void LLCharacter::updateMotions(e_update_t update_type)
}
bool force_update = (update_type == FORCE_UPDATE);
{
LL_RECORD_BLOCK_TIME(FTM_UPDATE_MOTIONS);
mMotionController.updateMotions(force_update);
}
}

View File

@ -163,6 +163,7 @@ BOOL LLEditingMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED;
LLVector3 focus_pt;
LLVector3* pointAtPt = (LLVector3*)mCharacter->getAnimationData("PointAtPoint");

View File

@ -42,9 +42,11 @@
//-----------------------------------------------------------------------------
// class LLEditingMotion
//-----------------------------------------------------------------------------
LL_ALIGN_PREFIX(16)
class LLEditingMotion :
public LLMotion
{
LL_ALIGN_NEW
public:
// Constructor
LLEditingMotion(const LLUUID &id);
@ -108,6 +110,13 @@ public:
//-------------------------------------------------------------------------
// joint states to be animated
//-------------------------------------------------------------------------
LL_ALIGN_16(LLJoint mParentJoint);
LL_ALIGN_16(LLJoint mShoulderJoint);
LL_ALIGN_16(LLJoint mElbowJoint);
LL_ALIGN_16(LLJoint mWristJoint);
LL_ALIGN_16(LLJoint mTarget);
LLJointSolverRP3 mIKSolver;
LLCharacter *mCharacter;
LLVector3 mWristOffset;
@ -117,17 +126,10 @@ public:
LLPointer<LLJointState> mWristState;
LLPointer<LLJointState> mTorsoState;
LLJoint mParentJoint;
LLJoint mShoulderJoint;
LLJoint mElbowJoint;
LLJoint mWristJoint;
LLJoint mTarget;
LLJointSolverRP3 mIKSolver;
static S32 sHandPose;
static S32 sHandPosePriority;
LLVector3 mLastSelectPt;
};
} LL_ALIGN_POSTFIX(16);
#endif // LL_LLKEYFRAMEMOTION_H

View File

@ -121,6 +121,7 @@ BOOL LLHandMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLHandMotion::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED;
eHandPose *requestedHandPose;
F32 timeDelta = time - mLastTime;

View File

@ -175,6 +175,7 @@ BOOL LLHeadRotMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLHeadRotMotion::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
LLQuaternion targetHeadRotWorld;
LLQuaternion currentRootRotWorld = mRootJoint->getWorldRotation();
LLQuaternion currentInvRootRotWorld = ~currentRootRotWorld;
@ -461,6 +462,7 @@ void LLEyeMotion::adjustEyeTarget(LLVector3* targetPos, LLJointState& left_eye_s
//-----------------------------------------------------------------------------
BOOL LLEyeMotion::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
//calculate jitter
if (mEyeJitterTimer.getElapsedTimeF32() > mEyeJitterTime)
{

View File

@ -944,6 +944,13 @@ const LLMatrix4 &LLJoint::getWorldMatrix()
return mXform.getWorldMatrix();
}
const LLMatrix4a& LLJoint::getWorldMatrix4a()
{
updateWorldMatrixParent();
return mWorldMatrix;
}
//--------------------------------------------------------------------
// setWorldMatrix()
@ -1025,6 +1032,7 @@ void LLJoint::updateWorldMatrix()
{
sNumUpdates++;
mXform.updateMatrix(FALSE);
mWorldMatrix.loadu(mXform.getWorldMatrix());
mDirtyFlags = 0x0;
}
}

View File

@ -38,6 +38,7 @@
#include "m4math.h"
#include "llquaternion.h"
#include "xform.h"
#include "llmatrix4a.h"
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
struct JointKey
@ -111,8 +112,10 @@ inline bool operator!=(const LLVector3OverrideMap& a, const LLVector3OverrideMap
//-----------------------------------------------------------------------------
// class LLJoint
//-----------------------------------------------------------------------------
LL_ALIGN_PREFIX(16)
class LLJoint
{
LL_ALIGN_NEW
public:
// priority levels, from highest to lowest
enum JointPriority
@ -140,16 +143,17 @@ public:
SUPPORT_EXTENDED
};
protected:
std::string mName;
// explicit transformation members
LL_ALIGN_16(LLMatrix4a mWorldMatrix);
LLXformMatrix mXform;
std::string mName;
SupportCategory mSupport;
// parent joint
LLJoint *mParent;
// explicit transformation members
LLXformMatrix mXform;
LLVector3 mDefaultPosition;
LLVector3 mDefaultScale;
@ -285,6 +289,8 @@ public:
const LLMatrix4 &getWorldMatrix();
void setWorldMatrix( const LLMatrix4& mat );
const LLMatrix4a& getWorldMatrix4a();
void updateWorldMatrixChildren();
void updateWorldMatrixParent();
@ -322,6 +328,6 @@ public:
// These are used in checks of whether a pos/scale override is considered significant.
bool aboveJointPosThreshold(const LLVector3& pos) const;
bool aboveJointScaleThreshold(const LLVector3& scale) const;
};
} LL_ALIGN_POSTFIX(16);
#endif // LL_LLJOINT_H

View File

@ -1,6 +1,6 @@
/**
* @file lljointsolverrp3.cpp
* @brief Implementation of LLJointSolverRP3 class.
* @brief Implementation of Joint Solver in 3D Real Projective space (RP3). See: https://en.wikipedia.org/wiki/Real_projective_space
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
@ -35,6 +35,11 @@
#define F_EPSILON 0.00001f
#if LL_RELEASE
#define DEBUG_JOINT_SOLVER 0
#else
#define DEBUG_JOINT_SOLVER 1
#endif
//-----------------------------------------------------------------------------
// Constructor
@ -150,6 +155,7 @@ void LLJointSolverRP3::solve()
LLVector3 cPos = mJointC->getWorldPosition();
LLVector3 gPos = mJointGoal->getWorldPosition();
#if DEBUG_JOINT_SOLVER
LL_DEBUGS("JointSolver") << "LLJointSolverRP3::solve()" << LL_NEWLINE
<< "bPosLocal = " << mJointB->getPosition() << LL_NEWLINE
<< "cPosLocal = " << mJointC->getPosition() << LL_NEWLINE
@ -159,6 +165,7 @@ void LLJointSolverRP3::solve()
<< "bPos : " << bPos << LL_NEWLINE
<< "cPos : " << cPos << LL_NEWLINE
<< "gPos : " << gPos << LL_ENDL;
#endif
//-------------------------------------------------------------------------
// get the poleVector in world space
@ -194,6 +201,7 @@ void LLJointSolverRP3::solve()
//-------------------------------------------------------------------------
LLVector3 abacCompOrthoVec = abVec - acVec * ((abVec * acVec)/(acVec * acVec));
#if DEBUG_JOINT_SOLVER
LL_DEBUGS("JointSolver") << "abVec : " << abVec << LL_NEWLINE
<< "bcVec : " << bcVec << LL_NEWLINE
<< "acVec : " << acVec << LL_NEWLINE
@ -202,6 +210,7 @@ void LLJointSolverRP3::solve()
<< "bcLen : " << bcLen << LL_NEWLINE
<< "agLen : " << agLen << LL_NEWLINE
<< "abacCompOrthoVec : " << abacCompOrthoVec << LL_ENDL;
#endif
//-------------------------------------------------------------------------
// compute the normal of the original ABC plane (and store for later)
@ -269,6 +278,7 @@ void LLJointSolverRP3::solve()
LLQuaternion bRot(theta - abbcAng, abbcOrthoVec);
#if DEBUG_JOINT_SOLVER
LL_DEBUGS("JointSolver") << "abbcAng : " << abbcAng << LL_NEWLINE
<< "abbcOrthoVec : " << abbcOrthoVec << LL_NEWLINE
<< "agLenSq : " << agLenSq << LL_NEWLINE
@ -280,6 +290,7 @@ void LLJointSolverRP3::solve()
<< abbcAng*180.0f/F_PI << " "
<< (theta - abbcAng)*180.0f/F_PI
<< LL_ENDL;
#endif
//-------------------------------------------------------------------------
// compute rotation that rotates new A->C to A->G
@ -293,9 +304,11 @@ void LLJointSolverRP3::solve()
LLQuaternion cgRot;
cgRot.shortestArc( acVec, agVec );
#if DEBUG_JOINT_SOLVER
LL_DEBUGS("JointSolver") << "bcVec : " << bcVec << LL_NEWLINE
<< "acVec : " << acVec << LL_NEWLINE
<< "cgRot : " << cgRot << LL_ENDL;
#endif
// update A->B and B->C with rotation from C to G
abVec = abVec * cgRot;
@ -358,11 +371,13 @@ void LLJointSolverRP3::solve()
//-------------------------------------------------------------------------
LLQuaternion twistRot( mTwist, agVec );
#if DEBUG_JOINT_SOLVER
LL_DEBUGS("JointSolver") << "abcNorm = " << abcNorm << LL_NEWLINE
<< "apgNorm = " << apgNorm << LL_NEWLINE
<< "pRot = " << pRot << LL_NEWLINE
<< "twist : " << mTwist*180.0/F_PI << LL_NEWLINE
<< "twistRot : " << twistRot << LL_ENDL;
#endif
//-------------------------------------------------------------------------
// compute rotation of A

View File

@ -121,6 +121,7 @@ BOOL LLKeyframeFallMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLKeyframeFallMotion::onUpdate(F32 activeTime, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
BOOL result = LLKeyframeMotion::onUpdate(activeTime, joint_mask);
F32 slerp_amt = clamp_rescale(activeTime / getDuration(), 0.5f, 0.75f, 0.f, 1.f);

View File

@ -680,6 +680,7 @@ BOOL LLKeyframeMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLKeyframeMotion::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
// llassert(time >= 0.f); // This will fire
time = llmax(0.f, time);

View File

@ -161,6 +161,7 @@ BOOL LLKeyframeMotionParam::onActivate()
//-----------------------------------------------------------------------------
BOOL LLKeyframeMotionParam::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED;
F32 weightFactor = 1.f / (F32)mParameterizedMotions.size();
// zero out all pose weights

View File

@ -37,9 +37,11 @@
//-----------------------------------------------------------------------------
// class LLKeyframeStandMotion
//-----------------------------------------------------------------------------
LL_ALIGN_PREFIX(16)
class LLKeyframeStandMotion :
public LLKeyframeMotion
{
LL_ALIGN_NEW
public:
// Constructor
LLKeyframeStandMotion(const LLUUID &id);
@ -69,6 +71,18 @@ public:
//-------------------------------------------------------------------------
// Member Data
//-------------------------------------------------------------------------
LLJoint mPelvisJoint;
LLJoint mHipLeftJoint;
LLJoint mKneeLeftJoint;
LLJoint mAnkleLeftJoint;
LLJoint mTargetLeft;
LLJoint mHipRightJoint;
LLJoint mKneeRightJoint;
LLJoint mAnkleRightJoint;
LLJoint mTargetRight;
LLCharacter *mCharacter;
BOOL mFlipFeet;
@ -83,18 +97,6 @@ public:
LLPointer<LLJointState> mKneeRightState;
LLPointer<LLJointState> mAnkleRightState;
LLJoint mPelvisJoint;
LLJoint mHipLeftJoint;
LLJoint mKneeLeftJoint;
LLJoint mAnkleLeftJoint;
LLJoint mTargetLeft;
LLJoint mHipRightJoint;
LLJoint mKneeRightJoint;
LLJoint mAnkleRightJoint;
LLJoint mTargetRight;
LLJointSolverRP3 mIKLeft;
LLJointSolverRP3 mIKRight;
@ -110,7 +112,7 @@ public:
BOOL mTrackAnkles;
S32 mFrameNum;
};
} LL_ALIGN_POSTFIX(16);
#endif // LL_LLKEYFRAMESTANDMOTION_H

View File

@ -105,6 +105,7 @@ void LLKeyframeWalkMotion::onDeactivate()
//-----------------------------------------------------------------------------
BOOL LLKeyframeWalkMotion::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED;
// compute time since last update
F32 deltaTime = time - mRealTimeLast;
@ -198,6 +199,7 @@ BOOL LLWalkAdjustMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED;
// delta_time is guaranteed to be non zero
F32 delta_time = llclamp(time - mLastTime, TIME_EPSILON, MAX_TIME_DELTA);
mLastTime = time;
@ -376,6 +378,7 @@ BOOL LLFlyAdjustMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLFlyAdjustMotion::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED;
LLVector3 ang_vel = mCharacter->getCharacterAngularVelocity() * mCharacter->getTimeDilation();
F32 speed = mCharacter->getCharacterVelocity().magVec();

View File

@ -513,6 +513,7 @@ void LLMotionController::resetJointSignatures()
//-----------------------------------------------------------------------------
void LLMotionController::updateIdleMotion(LLMotion* motionp)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration())
{
deactivateMotionInstance(motionp);
@ -551,6 +552,7 @@ void LLMotionController::updateIdleMotion(LLMotion* motionp)
//-----------------------------------------------------------------------------
void LLMotionController::updateIdleActiveMotions()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
for (motion_list_t::iterator iter = mActiveMotions.begin();
iter != mActiveMotions.end(); )
{
@ -563,10 +565,9 @@ void LLMotionController::updateIdleActiveMotions()
//-----------------------------------------------------------------------------
// updateMotionsByType()
//-----------------------------------------------------------------------------
static LLTrace::BlockTimerStatHandle FTM_MOTION_ON_UPDATE("Motion onUpdate");
void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_type)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
BOOL update_result = TRUE;
U8 last_joint_signature[LL_CHARACTER_MAX_ANIMATED_JOINTS];
@ -722,7 +723,6 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
// perform motion update
{
LL_RECORD_BLOCK_TIME(FTM_MOTION_ON_UPDATE);
update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
}
}
@ -778,6 +778,7 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
//-----------------------------------------------------------------------------
void LLMotionController::updateLoadingMotions()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
// query pending motions for completion
for (motion_set_t::iterator iter = mLoadingMotions.begin();
iter != mLoadingMotions.end(); )
@ -825,6 +826,7 @@ void LLMotionController::updateLoadingMotions()
//-----------------------------------------------------------------------------
void LLMotionController::updateMotions(bool force_update)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
// SL-763: "Distant animated objects run at super fast speed"
// The use_quantum optimization or possibly the associated code in setTimeStamp()
// does not work as implemented.
@ -920,6 +922,7 @@ void LLMotionController::updateMotions(bool force_update)
//-----------------------------------------------------------------------------
void LLMotionController::updateMotionsMinimal()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
// Always update mPrevTimerElapsed
mPrevTimerElapsed = mTimer.getElapsedTimeF32();
@ -937,6 +940,7 @@ void LLMotionController::updateMotionsMinimal()
//-----------------------------------------------------------------------------
BOOL LLMotionController::activateMotionInstance(LLMotion *motion, F32 time)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
// It's not clear why the getWeight() line seems to be crashing this, but
// hopefully this fixes it.
if (motion == NULL || motion->getPose() == NULL)

View File

@ -80,8 +80,10 @@ public:
const S32 JSB_NUM_JOINT_STATES = 6;
LL_ALIGN_PREFIX(16)
class LLJointStateBlender
{
LL_ALIGN_NEW
protected:
LLPointer<LLJointState> mJointStates[JSB_NUM_JOINT_STATES];
S32 mPriorities[JSB_NUM_JOINT_STATES];
@ -96,8 +98,8 @@ public:
void resetCachedJoint();
public:
LLJoint mJointCache;
};
LL_ALIGN_16(LLJoint mJointCache);
} LL_ALIGN_POSTFIX(16);
class LLMotion;

View File

@ -103,6 +103,7 @@ BOOL LLTargetingMotion::onActivate()
//-----------------------------------------------------------------------------
BOOL LLTargetingMotion::onUpdate(F32 time, U8* joint_mask)
{
LL_PROFILE_ZONE_SCOPED;
F32 slerp_amt = LLSmoothInterpolation::getInterpolant(TORSO_TARGET_HALF_LIFE);
LLVector3 target;

View File

@ -12,6 +12,7 @@ include(JsonCpp)
include(Copy3rdPartyLibs)
include(ZLIBNG)
include(URIPARSER)
include(Tracy)
include_directories( SYSTEM
${EXPAT_INCLUDE_DIRS}
@ -19,6 +20,7 @@ include_directories( SYSTEM
${JSONCPP_INCLUDE_DIR}
${ZLIBNG_INCLUDE_DIRS}
${URIPARSER_INCLUDE_DIRS}
${TRACY_INCLUDE_DIR}
)
# add_executable(lltreeiterators lltreeiterators.cpp)
@ -117,14 +119,16 @@ set(llcommon_SOURCE_FILES
lluriparser.cpp
lluuid.cpp
llworkerthread.cpp
timing.cpp
u64.cpp
threadpool.cpp
workqueue.cpp
StackWalker.cpp
)
set(llcommon_HEADER_FILES
CMakeLists.txt
chrono.h
classic_callback.h
ctype_workaround.h
fix_macros.h
@ -198,6 +202,8 @@ set(llcommon_HEADER_FILES
llmortician.h
llnametable.h
llpointer.h
llprofiler.h
llprofilercategories.h
llpounceable.h
llpredicate.h
llpreprocessor.h
@ -252,19 +258,20 @@ set(llcommon_HEADER_FILES
lockstatic.h
stdtypes.h
stringize.h
threadpool.h
threadsafeschedule.h
timer.h
tuple.h
u64.h
workqueue.h
StackWalker.h
)
# <FS:Beq> Tracy Profiler support
list(APPEND llcommon_SOURCE_FILES fstelemetry.cpp)
if (USE_TRACY_PROFILER)
list(APPEND llcommon_SOURCE_FILES fstracyclient.cpp)
endif()
# </FS:Beq> Tracy Profiler support
# <FS:ND> Add all nd* files. memory pool, intrinsics, ...
# <FS:Beq/> Tracy Profiler support
list(APPEND llcommon_SOURCE_FILES llprofiler.cpp)
# <FS:ND> Add all nd* files. memory pool, intrinsics, ...
SET( llcommon_ND_SOURCE_FILES
nd/ndexceptions.cpp
nd/ndlogthrottle.cpp
@ -342,6 +349,7 @@ target_link_libraries(
${BOOST_SYSTEM_LIBRARY}
${GOOGLE_PERFTOOLS_LIBRARIES}
${URIPARSER_LIBRARIES}
${TRACY_LIBRARY}
)
if (DARWIN)
@ -399,6 +407,9 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(threadsafeschedule "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(tuple "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(workqueue "" "${test_libs}")
## llexception_test.cpp isn't a regression test, and doesn't need to be run
## every build. It's to help a developer make implementation choices about

65
indra/llcommon/chrono.h Normal file
View File

@ -0,0 +1,65 @@
/**
* @file chrono.h
* @author Nat Goodspeed
* @date 2021-10-05
* @brief supplement <chrono> with utility functions
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_CHRONO_H)
#define LL_CHRONO_H
#include <chrono>
#include <type_traits> // std::enable_if
namespace LL
{
// time_point_cast() is derived from https://stackoverflow.com/a/35293183
// without the iteration: we think errors in the ~1 microsecond range are
// probably acceptable.
// This variant is for the optimal case when the source and dest use the same
// clock: that case is handled by std::chrono.
template <typename DestTimePoint, typename SrcTimePoint,
typename std::enable_if<std::is_same<typename DestTimePoint::clock,
typename SrcTimePoint::clock>::value,
bool>::type = true>
DestTimePoint time_point_cast(const SrcTimePoint& time)
{
return std::chrono::time_point_cast<typename DestTimePoint::duration>(time);
}
// This variant is for when the source and dest use different clocks -- see
// the linked StackOverflow answer, also Howard Hinnant's, for more context.
template <typename DestTimePoint, typename SrcTimePoint,
typename std::enable_if<! std::is_same<typename DestTimePoint::clock,
typename SrcTimePoint::clock>::value,
bool>::type = true>
DestTimePoint time_point_cast(const SrcTimePoint& time)
{
// The basic idea is that we must adjust the passed time_point by the
// difference between the clocks' epochs. But since time_point doesn't
// expose its epoch, we fall back on what each of them thinks is now().
// However, since we necessarily make sequential calls to those now()
// functions, the answers differ not only by the cycles spent executing
// those calls, but by potential OS interruptions between them. Try to
// reduce that error by capturing the source clock time both before and
// after the dest clock, and splitting the difference. Of course an
// interruption between two of these now() calls without a comparable
// interruption between the other two will skew the result, but better is
// more expensive.
const auto src_before = typename SrcTimePoint::clock::now();
const auto dest_now = typename DestTimePoint::clock::now();
const auto src_after = typename SrcTimePoint::clock::now();
const auto src_diff = src_after - src_before;
const auto src_now = src_before + src_diff / 2;
return dest_now + (time - src_now);
}
} // namespace LL
#endif /* ! defined(LL_CHRONO_H) */

View File

@ -1,88 +0,0 @@
#pragma once
#ifndef FS_TELEMETRY_H_INCLUDED
#define FS_TELEMETRY_H_INCLUDED
/**
* @file fstelemetry.h
* @brief fstelemetry Telemetry abstraction for FS
*
* $LicenseInfo:firstyear=2021&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2021, The Phoenix Firestorm Project, 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
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
// define a simple set of empty macros that allow us to build without the Tracy profiler installed in 3p
// this is similar to the profiler abstraction used by LL but as they have no plans to release that any time soon we'll replace it
// Just a minimal set at the moment will add locks/gpu/memory and other stuff later.
// generic switch (in case we ever add others or incorporate commercial tools like RAD Games if LL were to share a license)
// turn off in the else statement below.
#define FS_HAS_TELEMETRY_SUPPORT
#ifdef TRACY_ENABLE // (Tracy open source telemetry)
#include "Tracy.hpp"
#define FSZone ZoneNamed( ___tracy_scoped_zone, FSTelemetry::active)
#define FSZoneN( name ) ZoneNamedN( ___tracy_scoped_zone, name, FSTelemetry::active)
#define FSZoneC( color ) ZoneNamedC( ___tracy_scoped_zone, color, FSTelemetry::active)
#define FSZoneNC( name, color ) ZoneNamedNC( ___tracy_scoped_zone, name, color, FSTelemetry::active)
#define FSZoneText( text, size ) ZoneText( text, size )
#define FSZoneValue( num_uint64 ) ZoneValue( num_uint64 )
#define FSPlot( name, value ) TracyPlot( name, value)
#define FSPlotSq( name, lastval, newval ) TracyPlot( name, lastval);TracyPlot( name, newval);
#define FSFrameMark FrameMark
#define FSFrameMarkStart( name ) FrameMarkStart( name )
#define FSFrameMarkEnd( name ) FrameMarkEnd( name )
#define FSThreadName( name ) tracy::SetThreadName( name )
#define FSMessageL ( message ) tracy::Profiler::Message( message, 0 )
#define FSTelemetryIsConnected TracyIsConnected
#else // (no telemetry)
// No we don't want no stinkin' telemetry. move along
#undef FS_HAS_TELEMETRY_SUPPORT
#define FSZone
#define FSZoneN( name )
#define FSZoneC( color )
#define FSZoneNC( name, color )
#define FSZoneText( text, size )
#define FSZoneValue( num_uint64 )
#define FSPlot( name, value )
#define FSPlotSq( name, lastval, newval )
#define FSFrameMark
#define FSFrameMarkStart( name )
#define FSFrameMarkEnd( name )
#define FSThreadName( name )
#define FSMessageL( message )
#define FSTelemetryIsConnected
#endif // TRACY_ENABLE
#include <chrono>
#include <array>
#include <unordered_map>
namespace FSTelemetry
{
extern bool active;
}// namespace FSTelemetry
#endif

View File

@ -1,53 +0,0 @@
//
// Tracy profiler
// ----------------
//
// For fast integration, compile and
// link with this source file (and none
// other) in your executable (or in the
// main DLL / shared object on multi-DLL
// projects).
//
// Define TRACY_ENABLE to enable profiler.
#include "common/TracySystem.cpp"
#ifdef TRACY_ENABLE
#ifdef LL_WINDOWS
# pragma warning(push, 0)
#endif
#include "common/tracy_lz4.cpp"
#include "client/TracyProfiler.cpp"
#include "client/TracyCallstack.cpp"
#include "client/TracySysTime.cpp"
#include "client/TracySysTrace.cpp"
#include "common/TracySocket.cpp"
#include "client/tracy_rpmalloc.cpp"
#include "client/TracyDxt1.cpp"
#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6
# include "libbacktrace/alloc.cpp"
# include "libbacktrace/dwarf.cpp"
# include "libbacktrace/fileline.cpp"
# include "libbacktrace/mmapio.cpp"
# include "libbacktrace/posix.cpp"
# include "libbacktrace/sort.cpp"
# include "libbacktrace/state.cpp"
# if TRACY_HAS_CALLSTACK == 4
# include "libbacktrace/macho.cpp"
# else
# include "libbacktrace/elf.cpp"
# endif
#endif
#ifdef LL_WINDOWS
# pragma comment(lib, "ws2_32.lib")
# pragma comment(lib, "dbghelp.lib")
# pragma warning(pop)
#endif
#endif

View File

@ -27,6 +27,14 @@
#ifndef LL_LINDEN_COMMON_H
#define LL_LINDEN_COMMON_H
#include "llprofiler.h"
#if TRACY_ENABLE // hooks for memory profiling
void *tracy_aligned_malloc(size_t size, size_t alignment);
void tracy_aligned_free(void *memblock);
#define _aligned_malloc(X, Y) tracy_aligned_malloc((X), (Y))
#define _aligned_free(X) tracy_aligned_free((X))
#endif
// *NOTE: Please keep includes here to a minimum!
//
// Files included here are included in every library .cpp file and

View File

@ -33,6 +33,67 @@
#include "lltracethreadrecorder.h"
#include "llcleanup.h"
thread_local bool gProfilerEnabled = false;
// <FS:Beq/> #if (TRACY_ENABLE)
#if (TRACY_ENABLE) && LL_PROFILER_ENABLE_TRACY_MEMORY
// Override new/delete for tracy memory profiling
void *operator new(size_t size)
{
void* ptr;
if (gProfilerEnabled)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
ptr = (malloc)(size);
}
else
{
ptr = (malloc)(size);
}
if (!ptr)
{
throw std::bad_alloc();
}
TracyAlloc(ptr, size);
return ptr;
}
void operator delete(void *ptr) noexcept
{
TracyFree(ptr);
if (gProfilerEnabled)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
(free)(ptr);
}
else
{
(free)(ptr);
}
}
// C-style malloc/free can't be so easily overridden, so we define tracy versions and use
// a pre-processor #define in linden_common.h to redirect to them. The parens around the native
// functions below prevents recursive substitution by the preprocessor.
//
// Unaligned mallocs are rare in LL code but hooking them causes problems in 3p lib code (looking at
// you, Havok), so we'll only capture the aligned version.
void *tracy_aligned_malloc(size_t size, size_t alignment)
{
auto ptr = ll_aligned_malloc_fallback(size, alignment);
if (ptr) TracyAlloc(ptr, size);
return ptr;
}
void tracy_aligned_free(void *memblock)
{
TracyFree(memblock);
ll_aligned_free_fallback(memblock);
}
#endif
//static
BOOL LLCommon::sAprInitialized = FALSE;

View File

@ -53,6 +53,8 @@ private:
LLCoros::Mutex mMutex;
// Use LLCoros::ConditionVariable for the same reason.
LLCoros::ConditionVariable mCond;
using LockType = LLCoros::LockType;
using cv_status = LLCoros::cv_status;
public:
/// LLCond can be explicitly initialized with a specific value for mData if
@ -65,10 +67,29 @@ public:
LLCond(const LLCond&) = delete;
LLCond& operator=(const LLCond&) = delete;
/// get() returns a const reference to the stored DATA. The only way to
/// get a non-const reference -- to modify the stored DATA -- is via
/// update_one() or update_all().
const value_type& get() const { return mData; }
/**
* get() returns the stored DATA by value -- so to use get(), DATA must
* be copyable. The only way to get a non-const reference -- to modify
* the stored DATA -- is via update_one() or update_all().
*/
value_type get()
{
LockType lk(mMutex);
return mData;
}
/**
* get(functor) returns whatever the functor returns. It allows us to peek
* at the stored DATA without copying the whole thing. The functor must
* accept a const reference to DATA. If you want to modify DATA, call
* update_one() or update_all() instead.
*/
template <typename FUNC>
auto get(FUNC&& func)
{
LockType lk(mMutex);
return std::forward<FUNC>(func)(const_data());
}
/**
* Pass update_one() an invocable accepting non-const (DATA&). The
@ -80,11 +101,11 @@ public:
* update_one() when DATA is a struct or class.
*/
template <typename MODIFY>
void update_one(MODIFY modify)
void update_one(MODIFY&& modify)
{
{ // scope of lock can/should end before notify_one()
LLCoros::LockType lk(mMutex);
modify(mData);
LockType lk(mMutex);
std::forward<MODIFY>(modify)(mData);
}
mCond.notify_one();
}
@ -99,11 +120,11 @@ public:
* update_all() when DATA is a struct or class.
*/
template <typename MODIFY>
void update_all(MODIFY modify)
void update_all(MODIFY&& modify)
{
{ // scope of lock can/should end before notify_all()
LLCoros::LockType lk(mMutex);
modify(mData);
LockType lk(mMutex);
std::forward<MODIFY>(modify)(mData);
}
mCond.notify_all();
}
@ -116,9 +137,9 @@ public:
* wait() on the condition_variable.
*/
template <typename Pred>
void wait(Pred pred)
void wait(Pred&& pred)
{
LLCoros::LockType lk(mMutex);
LockType lk(mMutex);
// We must iterate explicitly since the predicate accepted by
// condition_variable::wait() requires a different signature:
// condition_variable::wait() calls its predicate with no arguments.
@ -127,7 +148,7 @@ public:
// But what if they instead pass a predicate accepting non-const
// (DATA&)? Such a predicate could modify mData, which would be Bad.
// Forbid that.
while (! pred(const_cast<const value_type&>(mData)))
while (! std::forward<Pred>(pred)(const_data()))
{
mCond.wait(lk);
}
@ -144,7 +165,7 @@ public:
* returning true.
*/
template <typename Rep, typename Period, typename Pred>
bool wait_for(const std::chrono::duration<Rep, Period>& timeout_duration, Pred pred)
bool wait_for(const std::chrono::duration<Rep, Period>& timeout_duration, Pred&& pred)
{
// Instead of replicating wait_until() logic, convert duration to
// time_point and just call wait_until().
@ -153,7 +174,8 @@ public:
// wrong! We'd keep pushing the timeout time farther and farther into
// the future. This way, we establish a definite timeout time and
// stick to it.
return wait_until(std::chrono::steady_clock::now() + timeout_duration, pred);
return wait_until(std::chrono::steady_clock::now() + timeout_duration,
std::forward<Pred>(pred));
}
/**
@ -163,9 +185,9 @@ public:
* generic wait_for() method.
*/
template <typename Pred>
bool wait_for(F32Milliseconds timeout_duration, Pred pred)
bool wait_for(F32Milliseconds timeout_duration, Pred&& pred)
{
return wait_for(convert(timeout_duration), pred);
return wait_for(convert(timeout_duration), std::forward<Pred>(pred));
}
protected:
@ -183,6 +205,10 @@ protected:
}
private:
// It's important to pass a const ref to certain user-specified functors
// that aren't supposed to be able to modify mData.
const value_type& const_data() const { return mData; }
/**
* Pass wait_until() a chrono::time_point, indicating the time at which we
* should stop waiting, and a predicate accepting (const DATA&), returning
@ -203,21 +229,21 @@ private:
* honoring a fixed timeout.
*/
template <typename Clock, typename Duration, typename Pred>
bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time, Pred pred)
bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time, Pred&& pred)
{
LLCoros::LockType lk(mMutex);
LockType lk(mMutex);
// We advise the caller to pass a predicate accepting (const DATA&).
// But what if they instead pass a predicate accepting non-const
// (DATA&)? Such a predicate could modify mData, which would be Bad.
// Forbid that.
while (! pred(const_cast<const value_type&>(mData)))
while (! std::forward<Pred>(pred)(const_data()))
{
if (LLCoros::cv_status::timeout == mCond.wait_until(lk, timeout_time))
if (cv_status::timeout == mCond.wait_until(lk, timeout_time))
{
// It's possible that wait_until() timed out AND the predicate
// became true more or less simultaneously. Even though
// wait_until() timed out, check the predicate one more time.
return pred(const_cast<const value_type&>(mData));
return std::forward<Pred>(pred)(const_data());
}
}
return true;

View File

@ -86,11 +86,9 @@ std::string LLDate::asRFC1123() const
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
}
LLTrace::BlockTimerStatHandle FT_DATE_FORMAT("Date Format");
std::string LLDate::toHTTPDateString (std::string fmt) const
{
LL_RECORD_BLOCK_TIME(FT_DATE_FORMAT);
LL_PROFILE_ZONE_SCOPED;
time_t locSeconds = (time_t) mSecondsSinceEpoch;
struct tm * gmt = gmtime (&locSeconds);
@ -99,7 +97,7 @@ std::string LLDate::toHTTPDateString (std::string fmt) const
std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt)
{
LL_RECORD_BLOCK_TIME(FT_DATE_FORMAT);
LL_PROFILE_ZONE_SCOPED;
// avoid calling setlocale() unnecessarily - it's expensive.
static std::string prev_locale = "";

View File

@ -109,6 +109,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
int syslogPriority = LOG_CRIT;
switch (level) {
case LLError::LEVEL_DEBUG: syslogPriority = LOG_DEBUG; break;
@ -168,6 +169,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
if (LLError::getAlwaysFlush())
{
mFile << message << std::endl;
@ -196,7 +198,7 @@ namespace {
{
return LLError::getEnabledLogTypesMask() & 0x04;
}
LL_FORCE_INLINE std::string createBoldANSI()
{
std::string ansi_code;
@ -222,10 +224,10 @@ namespace {
LL_FORCE_INLINE std::string createANSI(const std::string& color)
{
std::string ansi_code;
ansi_code += '\033';
ansi_code += "[";
ansi_code += '\033';
ansi_code += "[";
ansi_code += "38;5;";
ansi_code += color;
ansi_code += color;
ansi_code += "m";
return ansi_code;
@ -234,6 +236,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
// The default colors for error, warn and debug are now a bit more pastel
// and easier to read on the default (black) terminal background but you
// now have the option to set the color of each via an environment variables:
@ -263,6 +266,7 @@ namespace {
}
else
{
LL_PROFILE_ZONE_NAMED("fprintf");
fprintf(stderr, "%s\n", message.c_str());
}
#if LL_WINDOWS
@ -275,6 +279,7 @@ namespace {
LL_FORCE_INLINE void writeANSI(const std::string& ansi_code, const std::string& message)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
static std::string s_ansi_bold = createBoldANSI(); // bold text
static std::string s_ansi_reset = createResetANSI(); // reset
// ANSI color code escape sequence, message, and reset in one fprintf call
@ -311,6 +316,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
mBuffer->addLine(message);
}
@ -337,6 +343,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
debugger_print(message);
}
};
@ -1221,6 +1228,7 @@ namespace
void writeToRecorders(const LLError::CallSite& site, const std::string& message)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
LLError::ELevel level = site.mLevel;
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
@ -1362,6 +1370,7 @@ namespace LLError
bool Log::shouldLog(CallSite& site)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
LLMutexTrylock lock(getMutex<LOG_MUTEX>(), 5);
if (!lock.isLocked())
{
@ -1406,6 +1415,7 @@ namespace LLError
void Log::flush(const std::ostringstream& out, const CallSite& site)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5);
if (!lock.isLocked())
{

View File

@ -35,7 +35,9 @@
#include "stdtypes.h"
#include "llprofiler.h"
#include "llpreprocessor.h"
#include <boost/static_assert.hpp>
// <FS:ND> Supress some false positives of PVS Studio.
@ -354,7 +356,8 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL;
#define lllog(level, once, ...) \
do { \
do { \
LL_PROFILE_ZONE_NAMED("lllog"); \
const char* tags[] = {"", ##__VA_ARGS__}; \
static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \
lllog_test_()

View File

@ -190,6 +190,7 @@ namespace LLError
{}
void recordMessage(LLError::ELevel level, const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED
mCallable(level, message);
}
private:

View File

@ -67,6 +67,8 @@ LLEventMatching::LLEventMatching(LLEventPump& source, const LLSD& pattern):
bool LLEventMatching::post(const LLSD& event)
{
LL_PROFILE_ZONE_SCOPED
if (! llsd_matches(mPattern, event).empty())
return false;
@ -88,6 +90,8 @@ LLEventTimeoutBase::LLEventTimeoutBase(LLEventPump& source):
void LLEventTimeoutBase::actionAfter(F32 seconds, const Action& action)
{
LL_PROFILE_ZONE_SCOPED
setCountdown(seconds);
mAction = action;
if (! mMainloop.connected())
@ -126,6 +130,8 @@ public:
void operator()()
{
LL_PROFILE_ZONE_SCOPED
mPump.post(mEvent);
}
@ -136,6 +142,8 @@ private:
void LLEventTimeoutBase::eventAfter(F32 seconds, const LLSD& event)
{
LL_PROFILE_ZONE_SCOPED
actionAfter(seconds, EventAfter(*this, event));
}
@ -152,6 +160,8 @@ void LLEventTimeoutBase::cancel()
bool LLEventTimeoutBase::tick(const LLSD&)
{
LL_PROFILE_ZONE_SCOPED
if (countdownElapsed())
{
cancel();
@ -189,7 +199,9 @@ LLEventTimer* LLEventTimeout::post_every(F32 period, const std::string& pump, co
{
return LLEventTimer::run_every(
period,
[pump, data](){ LLEventPumps::instance().obtain(pump).post(data); });
[pump, data](){
LL_PROFILE_ZONE_SCOPED
LLEventPumps::instance().obtain(pump).post(data); });
}
LLEventTimer* LLEventTimeout::post_at(const LLDate& time, const std::string& pump, const LLSD& data)
@ -221,6 +233,8 @@ LLEventBatch::LLEventBatch(LLEventPump& source, std::size_t size):
void LLEventBatch::flush()
{
LL_PROFILE_ZONE_SCOPED
// copy and clear mBatch BEFORE posting to avoid weird circularity effects
LLSD batch(mBatch);
mBatch.clear();
@ -229,6 +243,8 @@ void LLEventBatch::flush()
bool LLEventBatch::post(const LLSD& event)
{
LL_PROFILE_ZONE_SCOPED
mBatch.append(event);
// calling setSize(same) performs the very check we want
setSize(mBatchSize);
@ -267,6 +283,8 @@ void LLEventThrottleBase::flush()
// post() anything but an isUndefined(). This is what mPosts is for.
if (mPosts)
{
LL_PROFILE_ZONE_SCOPED
mPosts = 0;
alarmCancel();
// This is not to set our alarm; we are not yet requesting
@ -288,6 +306,8 @@ LLSD LLEventThrottleBase::pending() const
bool LLEventThrottleBase::post(const LLSD& event)
{
LL_PROFILE_ZONE_SCOPED
// Always capture most recent post() event data. If caller wants to
// aggregate multiple events, let them retrieve pending() and modify
// before calling post().
@ -411,6 +431,8 @@ LLEventBatchThrottle::LLEventBatchThrottle(LLEventPump& source, F32 interval, st
bool LLEventBatchThrottle::post(const LLSD& event)
{
LL_PROFILE_ZONE_SCOPED
// simply retrieve pending value and append the new event to it
LLSD partial = pending();
partial.append(event);
@ -447,6 +469,8 @@ LLEventLogProxy::LLEventLogProxy(LLEventPump& source, const std::string& name, b
bool LLEventLogProxy::post(const LLSD& event) /* override */
{
LL_PROFILE_ZONE_SCOPED
auto counter = mCounter++;
auto eventplus = event;
if (eventplus.type() == LLSD::TypeMap)

View File

@ -429,6 +429,8 @@ public:
// path, then stores it to mTarget.
virtual bool post(const LLSD& event)
{
LL_PROFILE_ZONE_SCOPED
// Extract the element specified by 'mPath' from 'event'. To perform a
// generic type-appropriate store through mTarget, construct an
// LLSDParam<T> and store that, thus engaging LLSDParam's custom

View File

@ -93,6 +93,8 @@ struct LLStopWhenHandled
template<typename InputIterator>
result_type operator()(InputIterator first, InputIterator last) const
{
LL_PROFILE_ZONE_SCOPED
for (InputIterator si = first; si != last; ++si)
{
try

View File

@ -97,6 +97,11 @@ static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop)
{
const auto stack = to_string(boost::stacktrace::stacktrace());
LL_WARNS() << "SEH Exception handled (that probably shouldn't be): Code " << code
<< "\n Stack trace: \n"
<< stack << LL_ENDL;
if (code == STATUS_MSC_EXCEPTION)
{
// C++ exception, go on

View File

@ -196,29 +196,30 @@ TimeBlockTreeNode& BlockTimerStatHandle::getTreeNode() const
}
void BlockTimer::bootstrapTimerTree()
{
for (auto& base : BlockTimerStatHandle::instance_snapshot())
{
// because of indirect derivation from LLInstanceTracker, have to downcast
BlockTimerStatHandle& timer = static_cast<BlockTimerStatHandle&>(base);
if (&timer == &BlockTimer::getRootTimeBlock()) continue;
for (auto& base : BlockTimerStatHandle::instance_snapshot())
{
// because of indirect derivation from LLInstanceTracker, have to downcast
BlockTimerStatHandle& timer = static_cast<BlockTimerStatHandle&>(base);
if (&timer == &BlockTimer::getRootTimeBlock()) continue;
// bootstrap tree construction by attaching to last timer to be on stack
// when this timer was called
if (timer.getParent() == &BlockTimer::getRootTimeBlock())
{
TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator();
// bootstrap tree construction by attaching to last timer to be on stack
// when this timer was called
if (timer.getParent() == &BlockTimer::getRootTimeBlock())
{
TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator();
if (accumulator.mLastCaller)
{
timer.setParent(accumulator.mLastCaller);
accumulator.mParent = accumulator.mLastCaller;
}
// no need to push up tree on first use, flag can be set spuriously
accumulator.mMoveUpTree = false;
}
}
if (accumulator.mLastCaller)
{
timer.setParent(accumulator.mLastCaller);
accumulator.mParent = accumulator.mLastCaller;
}
// no need to push up tree on first use, flag can be set spuriously
accumulator.mMoveUpTree = false;
}
}
}
// bump timers up tree if they have been flagged as being in the wrong place
@ -226,6 +227,7 @@ void BlockTimer::bootstrapTimerTree()
// this preserves partial order derived from current frame's observations
void BlockTimer::incrementalUpdateTimerTree()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
for(block_timer_tree_df_post_iterator_t it = begin_block_timer_tree_df_post(BlockTimer::getRootTimeBlock());
it != end_block_timer_tree_df_post();
++it)
@ -265,7 +267,8 @@ void BlockTimer::incrementalUpdateTimerTree()
void BlockTimer::updateTimes()
{
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
// walk up stack of active timers and accumulate current time while leaving timing structures active
BlockTimerStackRecord* stack_record = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance();
if (!stack_record) return;
@ -276,7 +279,7 @@ void BlockTimer::updateTimes()
while(cur_timer
&& cur_timer->mParentTimerData.mActiveTimer != cur_timer) // root defined by parent pointing to self
{
{
U64 cumulative_time_delta = cur_time - cur_timer->mStartTime;
cur_timer->mStartTime = cur_time;

View File

@ -38,27 +38,10 @@
#define LL_FAST_TIMER_ON 1
#define LL_FASTTIMER_USE_RDTSC 1
// <FS:Beq> Add Tracy profiler support
/*
#define LL_RECORD_BLOCK_TIME(timer_stat) \
const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__);
*/
#include "fstelemetry.h"
#ifdef TRACY_ENABLE
// #undef TRACY_NO_FASTTIMERS // Uncomment if you want FASTTIMERS as well.
#ifdef TRACY_NO_FASTTIMERS
#define LL_RECORD_BLOCK_TIME(timer_stat) \
FSZoneN( #timer_stat );
#else // TRACY_NO_FASTTIMERS
#define LL_RECORD_BLOCK_TIME(timer_stat) \
FSZoneN( #timer_stat ); \
const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__);
#endif // TRACY_NO_FASTTIMERS
#else // TRACY_ENABLE
#define LL_RECORD_BLOCK_TIME(timer_stat) \
const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__);
#endif // TRACY_ENABLE
// </FS:Beq>
// NOTE: Also see llprofiler.h
#if !defined(LL_PROFILER_CONFIGURATION)
#define LL_RECORD_BLOCK_TIME(timer_stat) const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__);
#endif // LL_PROFILER_CONFIGURATION
namespace LLTrace
{

View File

@ -29,6 +29,11 @@
#include "llframetimer.h"
// We don't bother building a stand alone lib; we just need to include the one source file for Tracy support
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
#include "TracyClient.cpp"
#endif // LL_PROFILER_CONFIGURATION
// Static members
//LLTimer LLFrameTimer::sInternalTimer;
U64 LLFrameTimer::sStartTotalTime = totalTime();

View File

@ -83,13 +83,34 @@ class LLInstanceTracker
typedef llthread::LockStatic<StaticData> LockStatic;
public:
using ptr_t = std::shared_ptr<T>;
using weak_t = std::weak_ptr<T>;
/**
* Storing a dumb T* somewhere external is a bad idea, since
* LLInstanceTracker subclasses are explicitly destroyed rather than
* managed by smart pointers. It's legal to declare stack instances of an
* LLInstanceTracker subclass. But it's reasonable to store a
* std::weak_ptr<T>, which will become invalid when the T instance is
* destroyed.
*/
weak_t getWeak()
{
return mSelf;
}
static S32 instanceCount()
{
return LockStatic()->mMap.size();
}
// snapshot of std::pair<const KEY, std::shared_ptr<T>> pairs
class snapshot
{
// It's very important that what we store in this snapshot are
// weak_ptrs, NOT shared_ptrs. That's how we discover whether any
// instance has been deleted during the lifespan of a snapshot.
typedef std::vector<std::pair<const KEY, std::weak_ptr<T>>> VectorType;
typedef std::vector<std::pair<const KEY, weak_t>> VectorType;
// Dereferencing our iterator produces a std::shared_ptr for each
// instance that still exists. Since we store weak_ptrs, that involves
// two chained transformations:
@ -98,7 +119,7 @@ public:
// It is very important that we filter lazily, that is, during
// traversal. Any one of our stored weak_ptrs might expire during
// traversal.
typedef std::pair<const KEY, std::shared_ptr<T>> strong_pair;
typedef std::pair<const KEY, ptr_t> strong_pair;
// Note for future reference: nat has not yet had any luck (up to
// Boost 1.67) trying to use boost::transform_iterator with a hand-
// coded functor, only with actual functions. In my experience, an
@ -202,17 +223,12 @@ public:
iterator end() { return iterator(snapshot::end(), key_getter); }
};
static T* getInstance(const KEY& k)
static ptr_t getInstance(const KEY& k)
{
LockStatic lock;
const InstanceMap& map(lock->mMap);
typename InstanceMap::const_iterator found = map.find(k);
return (found == map.end()) ? NULL : found->second.get();
}
static S32 instanceCount()
{
return LockStatic()->mMap.size();
return (found == map.end()) ? NULL : found->second;
}
protected:
@ -222,7 +238,9 @@ protected:
// shared_ptr, so give it a no-op deleter. We store shared_ptrs in our
// InstanceMap specifically so snapshot can store weak_ptrs so we can
// detect deletions during traversals.
std::shared_ptr<T> ptr(static_cast<T*>(this), [](T*){});
ptr_t ptr(static_cast<T*>(this), [](T*){});
// save corresponding weak_ptr for future reference
mSelf = ptr;
LockStatic lock;
add_(lock, key, ptr);
}
@ -257,7 +275,7 @@ private:
static std::string report(const char* key) { return report(std::string(key)); }
// caller must instantiate LockStatic
void add_(LockStatic& lock, const KEY& key, const std::shared_ptr<T>& ptr)
void add_(LockStatic& lock, const KEY& key, const ptr_t& ptr)
{
mInstanceKey = key;
InstanceMap& map = lock->mMap;
@ -281,7 +299,7 @@ private:
break;
}
}
std::shared_ptr<T> remove_(LockStatic& lock)
ptr_t remove_(LockStatic& lock)
{
InstanceMap& map = lock->mMap;
typename InstanceMap::iterator iter = map.find(mInstanceKey);
@ -295,6 +313,9 @@ private:
}
private:
// Storing a weak_ptr to self is a bit like deriving from
// std::enable_shared_from_this(), except more explicit.
weak_t mSelf;
KEY mInstanceKey;
};
@ -326,6 +347,9 @@ class LLInstanceTracker<T, void, KEY_COLLISION_BEHAVIOR>
typedef llthread::LockStatic<StaticData> LockStatic;
public:
using ptr_t = std::shared_ptr<T>;
using weak_t = std::weak_ptr<T>;
/**
* Storing a dumb T* somewhere external is a bad idea, since
* LLInstanceTracker subclasses are explicitly destroyed rather than
@ -334,12 +358,15 @@ public:
* std::weak_ptr<T>, which will become invalid when the T instance is
* destroyed.
*/
std::weak_ptr<T> getWeak()
weak_t getWeak()
{
return mSelf;
}
static S32 instanceCount() { return LockStatic()->mSet.size(); }
static S32 instanceCount()
{
return LockStatic()->mSet.size();
}
// snapshot of std::shared_ptr<T> pointers
class snapshot
@ -347,7 +374,7 @@ public:
// It's very important that what we store in this snapshot are
// weak_ptrs, NOT shared_ptrs. That's how we discover whether any
// instance has been deleted during the lifespan of a snapshot.
typedef std::vector<std::weak_ptr<T>> VectorType;
typedef std::vector<weak_t> VectorType;
// Dereferencing our iterator produces a std::shared_ptr for each
// instance that still exists. Since we store weak_ptrs, that involves
// two chained transformations:
@ -453,7 +480,7 @@ protected:
private:
// Storing a weak_ptr to self is a bit like deriving from
// std::enable_shared_from_this(), except more explicit.
std::weak_ptr<T> mSelf;
weak_t mSelf;
};
#endif

View File

@ -220,7 +220,7 @@ void LLLeapListener::getAPI(const LLSD& request) const
{
Response reply(LLSD(), request);
LLEventAPI* found = LLEventAPI::getInstance(request["api"]);
auto found = LLEventAPI::getInstance(request["api"]);
if (found)
{
reply["name"] = found->getName();

View File

@ -82,6 +82,7 @@ void LLMemory::initMaxHeapSizeGB(F32Gigabytes max_heap_size)
//static
void LLMemory::updateMemoryInfo()
{
LL_PROFILE_ZONE_SCOPED
#if LL_WINDOWS
PROCESS_MEMORY_COUNTERS counters;
@ -145,6 +146,7 @@ void* LLMemory::tryToAlloc(void* address, U32 size)
//static
void LLMemory::logMemoryInfo(BOOL update)
{
LL_PROFILE_ZONE_SCOPED
if(update)
{
updateMemoryInfo() ;

View File

@ -101,6 +101,29 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
#define LL_ALIGN_16(var) LL_ALIGN_PREFIX(16) var LL_ALIGN_POSTFIX(16)
#define LL_ALIGN_NEW \
public: \
void* operator new(size_t size) \
{ \
return ll_aligned_malloc_16(size); \
} \
\
void operator delete(void* ptr) \
{ \
ll_aligned_free_16(ptr); \
} \
\
void* operator new[](size_t size) \
{ \
return ll_aligned_malloc_16(size); \
} \
\
void operator delete[](void* ptr) \
{ \
ll_aligned_free_16(ptr); \
}
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
// for enable buffer overrun detection predefine LL_DEBUG_BUFFER_OVERRUN in current library
@ -113,8 +136,9 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
#else
inline void* ll_aligned_malloc_fallback( size_t size, int align )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
#if defined(LL_WINDOWS)
return _aligned_malloc(size, align);
void* ret = (_aligned_malloc)(size, align);
#else
char* aligned = NULL;
void* mem = malloc( size + (align - 1) + sizeof(void*) );
@ -125,14 +149,18 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
((void**)aligned)[-1] = mem;
}
return aligned;
void* ret = aligned;
#endif
LL_PROFILE_ALLOC(ret, size);
return ret;
}
inline void ll_aligned_free_fallback( void* ptr )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
LL_PROFILE_FREE(ptr);
#if defined(LL_WINDOWS)
_aligned_free(ptr);
(_aligned_free)(ptr);
#else
if (ptr)
{
@ -146,21 +174,24 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed with ll_aligned_free_16().
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
#if defined(LL_WINDOWS)
return _aligned_malloc(size, 16);
void* ret = _aligned_malloc(size, 16);
#elif defined(LL_DARWIN)
return malloc(size); // default osx malloc is 16 byte aligned.
void* ret = malloc(size); // default osx malloc is 16 byte aligned.
#else
void *rtn;
if (LL_LIKELY(0 == posix_memalign(&rtn, 16, size)))
return rtn;
else // bad alignment requested, or out of memory
return NULL;
void *ret;
if (0 != posix_memalign(&ret, 16, size))
return nullptr;
#endif
LL_PROFILE_ALLOC(ret, size);
return ret;
}
inline void ll_aligned_free_16(void *p)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
LL_PROFILE_FREE(p);
#if defined(LL_WINDOWS)
_aligned_free(p);
#elif defined(LL_DARWIN)
@ -172,10 +203,12 @@ inline void ll_aligned_free_16(void *p)
inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // returned hunk MUST be freed with ll_aligned_free_16().
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
LL_PROFILE_FREE(ptr);
#if defined(LL_WINDOWS)
return _aligned_realloc(ptr, size, 16);
void* ret = _aligned_realloc(ptr, size, 16);
#elif defined(LL_DARWIN)
return realloc(ptr,size); // default osx malloc is 16 byte aligned.
void* ret = realloc(ptr,size); // default osx malloc is 16 byte aligned.
#else
//FIXME: memcpy is SLOW
void* ret = ll_aligned_malloc_16(size);
@ -188,27 +221,31 @@ inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // r
}
ll_aligned_free_16(ptr);
}
return ret;
#endif
LL_PROFILE_ALLOC(ptr, size);
return ret;
}
inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed with ll_aligned_free_32().
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
#if defined(LL_WINDOWS)
return _aligned_malloc(size, 32);
void* ret = _aligned_malloc(size, 32);
#elif defined(LL_DARWIN)
return ll_aligned_malloc_fallback( size, 32 );
void* ret = ll_aligned_malloc_fallback( size, 32 );
#else
void *rtn;
if (LL_LIKELY(0 == posix_memalign(&rtn, 32, size)))
return rtn;
else // bad alignment requested, or out of memory
return NULL;
void *ret;
if (0 != posix_memalign(&ret, 32, size))
return nullptr;
#endif
LL_PROFILE_ALLOC(ret, size);
return ret;
}
inline void ll_aligned_free_32(void *p)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
LL_PROFILE_FREE(p);
#if defined(LL_WINDOWS)
_aligned_free(p);
#elif defined(LL_DARWIN)
@ -222,29 +259,35 @@ inline void ll_aligned_free_32(void *p)
template<size_t ALIGNMENT>
LL_FORCE_INLINE void* ll_aligned_malloc(size_t size)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
void* ret;
if (LL_DEFAULT_HEAP_ALIGN % ALIGNMENT == 0)
{
return malloc(size);
ret = malloc(size);
LL_PROFILE_ALLOC(ret, size);
}
else if (ALIGNMENT == 16)
{
return ll_aligned_malloc_16(size);
ret = ll_aligned_malloc_16(size);
}
else if (ALIGNMENT == 32)
{
return ll_aligned_malloc_32(size);
ret = ll_aligned_malloc_32(size);
}
else
{
return ll_aligned_malloc_fallback(size, ALIGNMENT);
ret = ll_aligned_malloc_fallback(size, ALIGNMENT);
}
return ret;
}
template<size_t ALIGNMENT>
LL_FORCE_INLINE void ll_aligned_free(void* ptr)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
if (ALIGNMENT == LL_DEFAULT_HEAP_ALIGN)
{
LL_PROFILE_FREE(ptr);
free(ptr);
}
else if (ALIGNMENT == 16)
@ -266,6 +309,7 @@ LL_FORCE_INLINE void ll_aligned_free(void* ptr)
//
inline void ll_memcpy_nonaliased_aligned_16(char* __restrict dst, const char* __restrict src, size_t bytes)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
assert(src != NULL);
assert(dst != NULL);
assert(bytes > 0);

View File

@ -44,6 +44,7 @@ LLMutex::~LLMutex()
void LLMutex::lock()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if(isSelfLocked())
{ //redundant lock
mCount++;
@ -65,6 +66,7 @@ void LLMutex::lock()
void LLMutex::unlock()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if (mCount > 0)
{ //not the root unlock
mCount--;
@ -85,6 +87,7 @@ void LLMutex::unlock()
bool LLMutex::isLocked()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if (!mMutex.try_lock())
{
return true;
@ -108,6 +111,7 @@ LLThread::id_t LLMutex::lockingThread() const
bool LLMutex::trylock()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if(isSelfLocked())
{ //redundant lock
mCount++;
@ -146,17 +150,20 @@ LLCondition::~LLCondition()
void LLCondition::wait()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
std::unique_lock< std::mutex > lock(mMutex);
mCond.wait(lock);
}
void LLCondition::signal()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
mCond.notify_one();
}
void LLCondition::broadcast()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
mCond.notify_all();
}
@ -166,6 +173,7 @@ LLMutexTrylock::LLMutexTrylock(LLMutex* mutex)
: mMutex(mutex),
mLocked(false)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if (mMutex)
mLocked = mMutex->trylock();
}
@ -174,6 +182,7 @@ LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms)
: mMutex(mutex),
mLocked(false)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if (!mMutex)
return;
@ -188,6 +197,7 @@ LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms)
LLMutexTrylock::~LLMutexTrylock()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if (mMutex && mLocked)
mMutex->unlock();
}
@ -199,6 +209,7 @@ LLMutexTrylock::~LLMutexTrylock()
//
LLScopedLock::LLScopedLock(std::mutex* mutex) : mMutex(mutex)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if(mutex)
{
mutex->lock();
@ -217,6 +228,7 @@ LLScopedLock::~LLScopedLock()
void LLScopedLock::unlock()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if(mLocked)
{
mMutex->unlock();

View File

@ -187,7 +187,9 @@
#define LL_DLLIMPORT
#endif // LL_WINDOWS
#if ! defined(LL_WINDOWS)
#if __clang__ || ! defined(LL_WINDOWS)
// Only on Windows, and only with the Microsoft compiler (vs. clang) is
// wchar_t potentially not a distinct type.
#define LL_WCHAR_T_NATIVE 1
#else // LL_WINDOWS
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros

View File

@ -44,20 +44,6 @@
#include "llsd.h"
#if LL_MSVC && _M_X64
# define LL_X86_64 1
# define LL_X86 1
#elif LL_MSVC && _M_IX86
# define LL_X86 1
#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) )
# define LL_X86_64 1
# define LL_X86 1
#elif LL_GNUC && ( defined(__i386__) )
# define LL_X86 1
#elif LL_GNUC && ( defined(__powerpc__) || defined(__ppc__) )
# define LL_PPC 1
#endif
class LLProcessorInfoImpl; // foward declaration for the mImpl;
namespace

View File

@ -29,6 +29,20 @@
#define LLPROCESSOR_H
#include "llunits.h"
#if LL_MSVC && _M_X64
# define LL_X86_64 1
# define LL_X86 1
#elif LL_MSVC && _M_IX86
# define LL_X86 1
#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) )
# define LL_X86_64 1
# define LL_X86 1
#elif LL_GNUC && ( defined(__i386__) )
# define LL_X86 1
#elif LL_GNUC && ( defined(__powerpc__) || defined(__ppc__) )
# define LL_PPC 1
#endif
class LLProcessorInfoImpl;
class LL_COMMON_API LLProcessorInfo

View File

@ -1,6 +1,6 @@
/**
* @file fstelemetry.cpp
* @brief fstelemetry Telemetry abstraction for FS
* @file llprofiler.cpp
* @brief llprofiler Telemetry abstraction
*
* $LicenseInfo:firstyear=2021&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
@ -24,8 +24,8 @@
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#include "fstelemetry.h"
namespace FSTelemetry
#include "llprofiler.h"
namespace LLProfiler
{
bool active{false};

218
indra/llcommon/llprofiler.h Normal file
View File

@ -0,0 +1,218 @@
/**
* @file llprofiler.h
* @brief Wrapper for Tracy and/or other profilers
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2021, 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$
*/
#ifndef LL_PROFILER_H
#define LL_PROFILER_H
// If you use the default macros LL_PROFILE_ZONE_SCOPED and LL_PROFILE_ZONE_NAMED to profile code ...
//
// void foo()
// {
// LL_PROFILE_ZONE_SCOPED;
// :
//
// {
// LL_PROFILE_ZONE_NAMED("widget bar");
// :
// }
// {
// LL_PROFILE_ZONE_NAMED("widget qux");
// :
// }
// }
//
// ... please be aware that ALL these will show up in a Tracy capture which can quickly exhaust memory.
// Instead, use LL_PROFILE_ZONE_SCOPED_CATEGORY_* and LL_PROFILE_ZONE_NAMED_CATEGORY_* to profile code ...
//
// void foo()
// {
// LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
// :
//
// {
// LL_PROFILE_ZONE_NAMED_CATEGORY_UI("widget bar");
// :
// }
// {
// LL_PROFILE_ZONE_NAMED_CATEGORY_UI("widget qux");
// :
// }
// }
//
// ... as these can be selectively turned on/off. This will minimize memory usage and visual clutter in a Tracy capture.
// See llprofiler_categories.h for more details on profiling categories.
#define LL_PROFILER_CONFIG_NONE 0 // No profiling
#define LL_PROFILER_CONFIG_FAST_TIMER 1 // Profiling on: Only Fast Timers
#define LL_PROFILER_CONFIG_TRACY 2 // Profiling on: Only Tracy
#define LL_PROFILER_CONFIG_TRACY_FAST_TIMER 3 // Profiling on: Fast Timers + Tracy
#ifndef LL_PROFILER_CONFIGURATION
#define LL_PROFILER_CONFIGURATION LL_PROFILER_CONFIG_FAST_TIMER
#endif
extern thread_local bool gProfilerEnabled;
#if defined(LL_PROFILER_CONFIGURATION) && (LL_PROFILER_CONFIGURATION > LL_PROFILER_CONFIG_NONE)
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
#define TRACY_ENABLE 1
// Normally these would be enabled but we want to be able to build any viewer with Tracy enabled and run the Tracy server on another machine
// They must be undefined in order to work across multiple machines
// #define TRACY_NO_BROADCAST 1
// #define TRACY_ONLY_LOCALHOST 1
#define TRACY_ONLY_IPV4 1
#include "Tracy.hpp"
// <FS:Beq> Fixed mutual exclusion issues with RAM and GPU. NOTE: This might still break on Apple in which case we'll need to restrict that platform
//// GPU Mutually exclusive with detailed memory tracing
// #define LL_PROFILER_ENABLE_TRACY_OPENGL 0
#define LL_PROFILER_ENABLE_TRACY_MEMORY 1
#define LL_PROFILER_ENABLE_TRACY_OPENGL 1
#endif
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY
#define LL_PROFILER_FRAME_END FrameMark
#define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name ); gProfilerEnabled = true;
#define LL_PROFILER_THREAD_BEGIN(name) FrameMarkStart( name ) // C string
#define LL_PROFILER_THREAD_END(name) FrameMarkEnd( name ) // C string
// <FS:Beq> revert change that obscures custom FTM zones. We may want to may FTM Zones unique in future.
// #define LL_RECORD_BLOCK_TIME(name) ZoneScoped // Want descriptive names; was: ZoneNamedN( ___tracy_scoped_zone, #name, gProfilerEnabled );
#define LL_RECORD_BLOCK_TIME(name) ZoneNamedN( ___tracy_scoped_zone, #name, gProfilerEnabled )
// </FS:Beq>
// <FS:Beq>
// #define LL_PROFILE_ZONE_NAMED(name) ZoneNamedN( ___tracy_scoped_zone, name, true )
// #define LL_PROFILE_ZONE_NAMED_COLOR(name,color) ZoneNamedNC( ___tracy_scopped_zone, name, color, true ) // RGB
// #define LL_PROFILE_ZONE_SCOPED ZoneScoped
#define LL_PROFILE_ZONE_NAMED(name) ZoneNamedN( ___tracy_scoped_zone, name, gProfilerEnabled )
#define LL_PROFILE_ZONE_NAMED_COLOR(name,color) ZoneNamedNC( ___tracy_scopped_zone, name, color, gProfilerEnabled ) // RGB
#define LL_PROFILE_ZONE_SCOPED ZoneNamed( ___tracy_scoped_zone, gProfilerEnabled ) // <FS:Beq/> Enable deferred collection through filters
// </FS:Beq>
#define LL_PROFILE_ZONE_NUM( val ) ZoneValue( val )
#define LL_PROFILE_ZONE_TEXT( text, size ) ZoneText( text, size )
#define LL_PROFILE_ZONE_ERR(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0XFF0000 ) // RGB yellow
#define LL_PROFILE_ZONE_INFO(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0X00FFFF ) // RGB cyan
#define LL_PROFILE_ZONE_WARN(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0x0FFFF00 ) // RGB red
#define LL_PROFILE_ALLOC(ptr, size) TracyAlloc(ptr, size)
#define LL_PROFILE_FREE(ptr) TracyFree(ptr)
// <FS:Beq> Additional FS Tracy macros
#define LL_PROFILE_ZONE_COLOR(color) ZoneNamedC( ___tracy_scoped_zone, color, gProfilerEnabled ) // <FS:Beq/> Additional Tracy macro
#define LL_PROFILE_PLOT( name, value ) TracyPlot( name, value)
#define LL_PROFILE_PLOT_SQ( name, prev, value ) TracyPlot(name,prev);TracyPlot( name, value)
#define LL_PROFILE_IS_CONNECTED TracyIsConnected
// </FS:Beq>
#endif
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_FAST_TIMER
#define LL_PROFILER_FRAME_END
#define LL_PROFILER_SET_THREAD_NAME( name ) (void)(name);
#define LL_PROFILER_THREAD_BEGIN(name) (void)(name); // Not supported
#define LL_PROFILER_THREAD_END(name) (void)(name); // Not supported
#define LL_RECORD_BLOCK_TIME(name) const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(name)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__);
#define LL_PROFILE_ZONE_NAMED(name) // LL_PROFILE_ZONE_NAMED is a no-op when Tracy is disabled
#define LL_PROFILE_ZONE_NAMED_COLOR(name,color) // LL_RECORD_BLOCK_TIME(name)
#define LL_PROFILE_ZONE_SCOPED // LL_PROFILE_ZONE_SCOPED is a no-op when Tracy is disabled
#define LL_PROFILE_ZONE_NUM( val ) (void)( val ); // Not supported
#define LL_PROFILE_ZONE_TEXT( text, size ) (void)( text ); void( size ); // Not supported
#define LL_PROFILE_ZONE_ERR(name) (void)(name); // Not supported
#define LL_PROFILE_ZONE_INFO(name) (void)(name); // Not supported
#define LL_PROFILE_ZONE_WARN(name) (void)(name); // Not supported
#define LL_PROFILE_ALLOC(ptr, size) (void)(ptr); (void)(size);
#define LL_PROFILE_FREE(ptr) (void)(ptr);
// <FS:Beq> Additional FS Tracy macros
#define LL_PROFILE_ZONE_COLOR(color)
#define LL_PROFILE_PLOT( name, value )
#define LL_PROFILE_PLOT_SQ( name, prev, value )
#define LL_PROFILE_IS_CONNECTED
// </FS:Beq>
#endif
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
#define LL_PROFILER_FRAME_END FrameMark
#define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name ); gProfilerEnabled = true;
#define LL_PROFILER_THREAD_BEGIN(name) FrameMarkStart( name ) // C string
#define LL_PROFILER_THREAD_END(name) FrameMarkEnd( name ) // C string
// <FS:Beq> revert change that obscures custom FTM zones.
// #define LL_RECORD_BLOCK_TIME(name) ZoneScoped const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(name)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__);
#define LL_RECORD_BLOCK_TIME(name) ZoneNamedN( ___tracy_scoped_zone, #name, gProfilerEnabled ); const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(name)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__);
// </FS:Beq>
// <FS:Beq>
// #define LL_PROFILE_ZONE_NAMED(name) ZoneNamedN( ___tracy_scoped_zone, name, true )
// #define LL_PROFILE_ZONE_NAMED_COLOR(name,color) ZoneNamedNC( ___tracy_scopped_zone, name, color, true ) // RGB
// #define LL_PROFILE_ZONE_SCOPED ZoneScoped
#define LL_PROFILE_ZONE_NAMED(name) ZoneNamedN( ___tracy_scoped_zone, name, gProfilerEnabled );
#define LL_PROFILE_ZONE_NAMED_COLOR(name,color) ZoneNamedNC( ___tracy_scopped_zone, name, color, gProfilerEnabled ) // RGB
#define LL_PROFILE_ZONE_SCOPED ZoneNamed( ___tracy_scoped_zone, gProfilerEnabled ) // <FS:Beq/> Enable deferred collection through filters
// </FS:Beq>
#define LL_PROFILE_ZONE_NUM( val ) ZoneValue( val )
#define LL_PROFILE_ZONE_TEXT( text, size ) ZoneText( text, size )
#define LL_PROFILE_ZONE_ERR(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0XFF0000 ) // RGB yellow
#define LL_PROFILE_ZONE_INFO(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0X00FFFF ) // RGB cyan
#define LL_PROFILE_ZONE_WARN(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0x0FFFF00 ) // RGB red
#define LL_PROFILE_ALLOC(ptr, size) TracyAlloc(ptr, size)
#define LL_PROFILE_FREE(ptr) TracyFree(ptr)
// <FS:Beq> Additional FS Tracy macros
#define LL_PROFILE_ZONE_COLOR(color) ZoneNamedC( ___tracy_scoped_zone, color, gProfilerEnabled )
#define LL_PROFILE_PLOT( name, value ) TracyPlot( name, value)
#define LL_PROFILE_PLOT_SQ( name, prev, value ) TracyPlot( name, prev );TracyPlot( name, value )
#define LL_PROFILE_IS_CONNECTED TracyIsConnected
// </FS:Beq>
#endif
#else
#define LL_PROFILER_FRAME_END
#define LL_PROFILER_SET_THREAD_NAME(name)
#define LL_PROFILER_THREAD_BEGIN(name)
#define LL_PROFILER_THREAD_END(name)
#define LL_RECORD_BLOCK_TIME(name)
#define LL_PROFILE_ZONE_NAMED(name)
#define LL_PROFILE_ZONE_NAMED_COLOR(name,color)
#define LL_PROFILE_ZONE_SCOPED
#define LL_PROFILE_ZONE_NUM(val)
#define LL_PROFILE_ZONE_TEXT(text, size)
#define LL_PROFILE_ZONE_ERR(name)
#define LL_PROFILE_ZONE_INFO(name)
#define LL_PROFILE_ZONE_WARN(name)
// <FS:Ansariel> Additional FS Tracy macros
#define LL_PROFILE_ZONE_COLOR(color)
#define LL_PROFILE_PLOT( name, value )
#define LL_PROFILE_PLOT_SQ( name, prev, value )
#define LL_PROFILE_IS_CONNECTED
// </FS:Ansariel>
#endif // LL_PROFILER
#include "llprofilercategories.h"
#endif // LL_PROFILER_H

View File

@ -0,0 +1,280 @@
/**
* @file llprofiler_ategories.h
* @brief Profiling categories to minimize Tracy memory usage when viewing captures.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, 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$
*/
#ifndef LL_PROFILER_CATEGORIES_H
#define LL_PROFILER_CATEGORIES_H
// A Tracy capture can quickly consume memory. Use these defines to selectively turn on/off Tracy profiling for these categories.
// The biggest memory usage ones are:
//
// LL_PROFILER_CATEGORY_ENABLE_DRAWPOOL
// LL_PROFILER_CATEGORY_ENABLE_LLSD
// LL_PROFILER_CATEGORY_ENABLE_MEMORY
// LL_PROFILER_CATEGORY_ENABLE_SHADERS
//
// NOTE: You can still manually use:
// LL_PROFILE_ZONE_SCOPED();
// LL_PROFILE_ZONE_NAMED("name");
// but just be aware that those will ALWAYS show up in a Tracy capture
// a) using more memory, and
// b) adding visual clutter.
#define LL_PROFILER_CATEGORY_ENABLE_APP 1
#define LL_PROFILER_CATEGORY_ENABLE_AVATAR 1
#define LL_PROFILER_CATEGORY_ENABLE_DISPLAY 1
#define LL_PROFILER_CATEGORY_ENABLE_DRAWABLE 1
#define LL_PROFILER_CATEGORY_ENABLE_DRAWPOOL 1
#define LL_PROFILER_CATEGORY_ENABLE_ENVIRONMENT 1
#define LL_PROFILER_CATEGORY_ENABLE_FACE 1
#define LL_PROFILER_CATEGORY_ENABLE_LLSD 1
#define LL_PROFILER_CATEGORY_ENABLE_LOGGING 1
#define LL_PROFILER_CATEGORY_ENABLE_MATERIAL 1
#define LL_PROFILER_CATEGORY_ENABLE_MEDIA 1
#define LL_PROFILER_CATEGORY_ENABLE_MEMORY 1
#define LL_PROFILER_CATEGORY_ENABLE_NETWORK 1
#define LL_PROFILER_CATEGORY_ENABLE_OCTREE 1
#define LL_PROFILER_CATEGORY_ENABLE_PIPELINE 1
#define LL_PROFILER_CATEGORY_ENABLE_SHADER 1
#define LL_PROFILER_CATEGORY_ENABLE_SPATIAL 1
#define LL_PROFILER_CATEGORY_ENABLE_STATS 1
#define LL_PROFILER_CATEGORY_ENABLE_STRING 1
#define LL_PROFILER_CATEGORY_ENABLE_TEXTURE 1
#define LL_PROFILER_CATEGORY_ENABLE_THREAD 1
#define LL_PROFILER_CATEGORY_ENABLE_UI 1
#define LL_PROFILER_CATEGORY_ENABLE_VIEWER 1
#define LL_PROFILER_CATEGORY_ENABLE_VERTEX 1
#define LL_PROFILER_CATEGORY_ENABLE_VOLUME 1
#define LL_PROFILER_CATEGORY_ENABLE_WIN32 1
#if LL_PROFILER_CATEGORY_ENABLE_APP
#define LL_PROFILE_ZONE_NAMED_CATEGORY_APP LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_APP LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_APP(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_APP
#endif
#if LL_PROFILER_CATEGORY_ENABLE_AVATAR
#define LL_PROFILE_ZONE_NAMED_CATEGORY_AVATAR LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_AVATAR(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR
#endif
#if LL_PROFILER_CATEGORY_ENABLE_DISPLAY
#define LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY
#endif
#if LL_PROFILER_CATEGORY_ENABLE_DRAWABLE
#define LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWABLE LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWABLE(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE
#endif
#if LL_PROFILER_CATEGORY_ENABLE_DRAWPOOL
#define LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL
#endif
#if LL_PROFILER_CATEGORY_ENABLE_ENVIRONMENT
#define LL_PROFILE_ZONE_NAMED_CATEGORY_ENVIRONMENT LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_ENVIRONMENT LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_ENVIRONMENT(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_ENVIRONMENT
#endif
#if LL_PROFILER_CATEGORY_ENABLE_FACE
#define LL_PROFILE_ZONE_NAMED_CATEGORY_FACE LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_FACE LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_FACE(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_FACE
#endif
#if LL_PROFILER_CATEGORY_ENABLE_LLSD
#define LL_PROFILE_ZONE_NAMED_CATEGORY_LLSD LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_LLSD(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
#endif
#if LL_PROFILER_CATEGORY_ENABLE_LOGGING
#define LL_PROFILE_ZONE_NAMED_CATEGORY_LOGGING LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_LOGGING(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
#endif
#if LL_PROFILER_CATEGORY_ENABLE_MATERIAL
#define LL_PROFILE_ZONE_NAMED_CATEGORY_MATERIAL LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_MATERIAL LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_MATERIAL(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_MATERIAL
#endif
#if LL_PROFILER_CATEGORY_ENABLE_MEDIA
#define LL_PROFILE_ZONE_NAMED_CATEGORY_MEDIA LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_MEDIA(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA
#endif
#if LL_PROFILER_CATEGORY_ENABLE_MEMORY
#define LL_PROFILE_ZONE_NAMED_CATEGORY_MEMORY LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_MEMORY(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY
#endif
#if LL_PROFILER_CATEGORY_ENABLE_NETWORK
#define LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK
#endif
#if LL_PROFILER_CATEGORY_ENABLE_OCTREE
#define LL_PROFILE_ZONE_NAMED_CATEGORY_OCTREE LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_OCTREE LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_OCTREE(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_OCTREE
#endif
#if LL_PROFILER_CATEGORY_ENABLE_PIPELINE
#define LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE
#endif
#if LL_PROFILER_CATEGORY_ENABLE_SHADER
#define LL_PROFILE_ZONE_NAMED_CATEGORY_SHADER LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_SHADER(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER
#endif
#if LL_PROFILER_CATEGORY_ENABLE_SPATIAL
#define LL_PROFILE_ZONE_NAMED_CATEGORY_SPATIAL LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_SPATIAL LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_SPATIAL(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_SPATIAL
#endif
#if LL_PROFILER_CATEGORY_ENABLE_STATS
#define LL_PROFILE_ZONE_NAMED_CATEGORY_STATS LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_STATS(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS
#endif
#if LL_PROFILER_CATEGORY_ENABLE_STRING
#define LL_PROFILE_ZONE_NAMED_CATEGORY_STRING LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_STRING(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING
#endif
#if LL_PROFILER_CATEGORY_ENABLE_TEXTURE
#define LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE
#endif
#if LL_PROFILER_CATEGORY_ENABLE_THREAD
#define LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
#endif
#if LL_PROFILER_CATEGORY_ENABLE_UI
#define LL_PROFILE_ZONE_NAMED_CATEGORY_UI LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_UI LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_UI(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_UI
#endif
#if LL_PROFILER_CATEGORY_ENABLE_VERTEX
#define LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX
#endif
#if LL_PROFILER_CATEGORY_ENABLE_VIEWER
#define LL_PROFILE_ZONE_NAMED_CATEGORY_VIEWER LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VIEWER LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_VIEWER(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VIEWER
#endif
#if LL_PROFILER_CATEGORY_ENABLE_VOLUME
#define LL_PROFILE_ZONE_NAMED_CATEGORY_VOLUME LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_VOLUME(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
#endif
#if LL_PROFILER_CATEGORY_ENABLE_WIN32
#define LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32 LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32 LL_PROFILE_ZONE_SCOPED
#else
#define LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32(name)
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32
#endif
#endif // LL_PROFILER_CATEGORIES_H

View File

@ -112,6 +112,8 @@ void LLQueuedThread::shutdown()
// virtual
S32 LLQueuedThread::update(F32 max_time_ms)
{
LL_PROFILE_ZONE_SCOPED
if (!mStarted)
{
if (!mThreaded)
@ -125,6 +127,8 @@ S32 LLQueuedThread::update(F32 max_time_ms)
S32 LLQueuedThread::updateQueue(F32 max_time_ms)
{
LL_PROFILE_ZONE_SCOPED
F64 max_time = (F64)max_time_ms * .001;
LLTimer timer;
S32 pending = 1;
@ -147,11 +151,14 @@ S32 LLQueuedThread::updateQueue(F32 max_time_ms)
break;
}
}
return pending;
}
void LLQueuedThread::incQueue()
{
LL_PROFILE_ZONE_SCOPED
// Something has been added to the queue
if (!isPaused())
{
@ -166,6 +173,8 @@ void LLQueuedThread::incQueue()
// May be called from any thread
S32 LLQueuedThread::getPending()
{
LL_PROFILE_ZONE_SCOPED
S32 res;
lockData();
res = mRequestQueue.size();
@ -176,6 +185,8 @@ S32 LLQueuedThread::getPending()
// MAIN thread
void LLQueuedThread::waitOnPending()
{
LL_PROFILE_ZONE_SCOPED
while(1)
{
update(0);
@ -195,6 +206,8 @@ void LLQueuedThread::waitOnPending()
// MAIN thread
void LLQueuedThread::printQueueStats()
{
LL_PROFILE_ZONE_SCOPED
lockData();
if (!mRequestQueue.empty())
{
@ -211,6 +224,8 @@ void LLQueuedThread::printQueueStats()
// MAIN thread
LLQueuedThread::handle_t LLQueuedThread::generateHandle()
{
LL_PROFILE_ZONE_SCOPED
lockData();
while ((mNextHandle == nullHandle()) || (mRequestHash.find(mNextHandle)))
{
@ -224,6 +239,8 @@ LLQueuedThread::handle_t LLQueuedThread::generateHandle()
// MAIN thread
bool LLQueuedThread::addRequest(QueuedRequest* req)
{
LL_PROFILE_ZONE_SCOPED
if (mStatus == QUITTING)
{
return false;
@ -246,6 +263,8 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
// MAIN thread
bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
{
LL_PROFILE_ZONE_SCOPED
llassert (handle != nullHandle());
bool res = false;
bool waspaused = isPaused();
@ -281,12 +300,15 @@ bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_co
{
pause();
}
return res;
}
// MAIN thread
LLQueuedThread::QueuedRequest* LLQueuedThread::getRequest(handle_t handle)
{
LL_PROFILE_ZONE_SCOPED
if (handle == nullHandle())
{
return 0;
@ -299,6 +321,8 @@ LLQueuedThread::QueuedRequest* LLQueuedThread::getRequest(handle_t handle)
LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
{
LL_PROFILE_ZONE_SCOPED
status_t res = STATUS_EXPIRED;
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
@ -312,6 +336,8 @@ LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
void LLQueuedThread::abortRequest(handle_t handle, bool autocomplete)
{
LL_PROFILE_ZONE_SCOPED
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
if (req)
@ -324,6 +350,8 @@ void LLQueuedThread::abortRequest(handle_t handle, bool autocomplete)
// MAIN thread
void LLQueuedThread::setFlags(handle_t handle, U32 flags)
{
LL_PROFILE_ZONE_SCOPED
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
if (req)
@ -335,6 +363,8 @@ void LLQueuedThread::setFlags(handle_t handle, U32 flags)
void LLQueuedThread::setPriority(handle_t handle, U32 priority)
{
LL_PROFILE_ZONE_SCOPED
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
if (req)
@ -357,6 +387,8 @@ void LLQueuedThread::setPriority(handle_t handle, U32 priority)
bool LLQueuedThread::completeRequest(handle_t handle)
{
LL_PROFILE_ZONE_SCOPED
bool res = false;
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
@ -401,6 +433,8 @@ bool LLQueuedThread::check()
S32 LLQueuedThread::processNextRequest()
{
LL_PROFILE_ZONE_SCOPED
QueuedRequest *req;
// Get next request from pool
lockData();
@ -517,9 +551,11 @@ void LLQueuedThread::run()
mIdleThread = FALSE;
LL_PROFILER_THREAD_BEGIN(mName.c_str())
threadedUpdate();
int pending_work = processNextRequest();
LL_PROFILER_THREAD_END(mName.c_str())
if (pending_work == 0)
{

View File

@ -29,6 +29,9 @@
#include "llerror.h"
// maximum reference count before sounding memory leak alarm
const S32 gMaxRefCount = 65536;
LLRefCount::LLRefCount(const LLRefCount& other)
: mRef(0)
{
@ -47,7 +50,7 @@ LLRefCount::LLRefCount() :
LLRefCount::~LLRefCount()
{
if (mRef != 0)
if (mRef != LL_REFCOUNT_FREE && mRef != 0)
{
LL_ERRS() << "deleting non-zero reference" << LL_ENDL;
}

View File

@ -37,6 +37,10 @@ class LLMutex;
// see llthread.h for LLThreadSafeRefCount
//----------------------------------------------------------------------------
//nonsense but recognizable value for freed LLRefCount (aids in debugging)
#define LL_REFCOUNT_FREE 1234567890
extern const S32 gMaxRefCount;
class LL_COMMON_API LLRefCount
{
protected:
@ -47,17 +51,25 @@ protected:
public:
LLRefCount();
inline void validateRefCount() const
{
llassert(mRef > 0); // ref count below 0, likely corrupted
llassert(mRef < gMaxRefCount); // ref count excessive, likely memory leak
}
inline void ref() const
{
mRef++;
validateRefCount();
}
inline S32 unref() const
{
llassert(mRef >= 1);
validateRefCount();
if (0 == --mRef)
{
delete this;
mRef = LL_REFCOUNT_FREE; // set to nonsense yet recognizable value to aid in debugging
delete this;
return 0;
}
return mRef;

View File

@ -400,6 +400,7 @@ namespace
ImplMap& ImplMap::makeMap(LLSD::Impl*& var)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
if (shared())
{
ImplMap* i = new ImplMap(mData);
@ -414,18 +415,21 @@ namespace
bool ImplMap::has(const LLSD::String& k) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
DataMap::const_iterator i = mData.find(k);
return i != mData.end();
}
LLSD ImplMap::get(const LLSD::String& k) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
DataMap::const_iterator i = mData.find(k);
return (i != mData.end()) ? i->second : LLSD();
}
LLSD ImplMap::getKeys() const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
LLSD keys = LLSD::emptyArray();
DataMap::const_iterator iter = mData.begin();
while (iter != mData.end())
@ -438,11 +442,13 @@ namespace
void ImplMap::insert(const LLSD::String& k, const LLSD& v)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
mData.insert(DataMap::value_type(k, v));
}
void ImplMap::erase(const LLSD::String& k)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
mData.erase(k);
}
@ -684,6 +690,7 @@ const LLSD::Impl& LLSD::Impl::safe(const Impl* impl)
ImplMap& LLSD::Impl::makeMap(Impl*& var)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
ImplMap* im = new ImplMap;
reset(var, im);
return *im;
@ -887,11 +894,16 @@ LLSD& LLSD::with(const String& k, const LLSD& v)
}
void LLSD::erase(const String& k) { makeMap(impl).erase(k); }
LLSD& LLSD::operator[](const String& k)
{ return makeMap(impl).ref(k); }
LLSD& LLSD::operator[](const String& k)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
return makeMap(impl).ref(k);
}
const LLSD& LLSD::operator[](const String& k) const
{ return safe(impl).ref(k); }
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
return safe(impl).ref(k);
}
LLSD LLSD::emptyArray()
{
@ -914,10 +926,16 @@ LLSD& LLSD::with(Integer i, const LLSD& v)
LLSD& LLSD::append(const LLSD& v) { return makeArray(impl).append(v); }
void LLSD::erase(Integer i) { makeArray(impl).erase(i); }
LLSD& LLSD::operator[](Integer i)
{ return makeArray(impl).ref(i); }
LLSD& LLSD::operator[](Integer i)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
return makeArray(impl).ref(i);
}
const LLSD& LLSD::operator[](Integer i) const
{ return safe(impl).ref(i); }
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
return safe(impl).ref(i);
}
static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat)
{

View File

@ -290,9 +290,17 @@ public:
LLSD& with(const String&, const LLSD&);
LLSD& operator[](const String&);
LLSD& operator[](const char* c) { return (*this)[String(c)]; }
LLSD& operator[](const char* c)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
return (*this)[String(c)];
}
const LLSD& operator[](const String&) const;
const LLSD& operator[](const char* c) const { return (*this)[String(c)]; }
const LLSD& operator[](const char* c) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
return (*this)[String(c)];
}
//@}
/** @name Array Values */

View File

@ -37,8 +37,6 @@ static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs;
static LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs;
static const LLSD NO_VALUE_MARKER;
LLTrace::BlockTimerStatHandle FTM_SD_PARAM_ADAPTOR("LLSD to LLInitParam conversion");
//
// LLParamSDParser
//

View File

@ -110,7 +110,6 @@ private:
};
extern LL_COMMON_API LLTrace::BlockTimerStatHandle FTM_SD_PARAM_ADAPTOR;
template<typename T>
class LLSDParamAdapter : public T
{
@ -118,7 +117,7 @@ public:
LLSDParamAdapter() {}
LLSDParamAdapter(const LLSD& sd)
{
LL_RECORD_BLOCK_TIME(FTM_SD_PARAM_ADAPTOR);
LL_PROFILE_ZONE_SCOPED;
LLParamSDParser parser;
// don't spam for implicit parsing of LLSD, as we want to allow arbitrary freeform data and ignore most of it
bool parse_silently = true;

View File

@ -221,6 +221,8 @@ BOOL compare_llsd_with_template(
const LLSD& template_llsd,
LLSD& resultant_llsd)
{
LL_PROFILE_ZONE_SCOPED
if (
llsd_to_test.isUndefined() &&
template_llsd.isDefined() )
@ -342,6 +344,8 @@ bool filter_llsd_with_template(
const LLSD & template_llsd,
LLSD & resultant_llsd)
{
LL_PROFILE_ZONE_SCOPED
if (llsd_to_test.isUndefined() && template_llsd.isDefined())
{
resultant_llsd = template_llsd;
@ -536,6 +540,8 @@ class TypeLookup
public:
TypeLookup()
{
LL_PROFILE_ZONE_SCOPED
for (const Data *di(boost::begin(typedata)), *dend(boost::end(typedata)); di != dend; ++di)
{
mMap[di->type] = di->name;
@ -544,6 +550,8 @@ public:
std::string lookup(LLSD::Type type) const
{
LL_PROFILE_ZONE_SCOPED
MapType::const_iterator found = mMap.find(type);
if (found != mMap.end())
{
@ -594,6 +602,8 @@ static std::string match_types(LLSD::Type expect, // prototype.type()
LLSD::Type actual, // type we're checking
const std::string& pfx) // as for llsd_matches
{
LL_PROFILE_ZONE_SCOPED
// Trivial case: if the actual type is exactly what we expect, we're good.
if (actual == expect)
return "";
@ -631,6 +641,8 @@ static std::string match_types(LLSD::Type expect, // prototype.type()
// see docstring in .h file
std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx)
{
LL_PROFILE_ZONE_SCOPED
// An undefined prototype means that any data is valid.
// An undefined slot in an array or map prototype means that any data
// may fill that slot.
@ -763,6 +775,8 @@ std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::str
bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits)
{
LL_PROFILE_ZONE_SCOPED
// We're comparing strict equality of LLSD representation rather than
// performing any conversions. So if the types aren't equal, the LLSD
// values aren't equal.
@ -871,6 +885,8 @@ namespace llsd
LLSD& drill_ref(LLSD& blob, const LLSD& rawPath)
{
LL_PROFILE_ZONE_SCOPED
// Treat rawPath uniformly as an array. If it's not already an array,
// store it as the only entry in one. (But let's say Undefined means an
// empty array.)
@ -896,6 +912,8 @@ LLSD& drill_ref(LLSD& blob, const LLSD& rawPath)
// path entry that's bad.
for (LLSD::Integer i = 0; i < path.size(); ++i)
{
LL_PROFILE_ZONE_NUM( i )
const LLSD& key{path[i]};
if (key.isString())
{
@ -924,6 +942,8 @@ LLSD& drill_ref(LLSD& blob, const LLSD& rawPath)
LLSD drill(const LLSD& blob, const LLSD& path)
{
LL_PROFILE_ZONE_SCOPED
// drill_ref() does exactly what we want. Temporarily cast away
// const-ness and use that.
return drill_ref(const_cast<LLSD&>(blob), path);
@ -936,6 +956,8 @@ LLSD drill(const LLSD& blob, const LLSD& path)
// filter may be include to exclude/include keys in a map.
LLSD llsd_clone(LLSD value, LLSD filter)
{
LL_PROFILE_ZONE_SCOPED
LLSD clone;
bool has_filter(filter.isMap());

View File

@ -496,6 +496,7 @@ public:
static DERIVED_TYPE* getInstance()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
// We know the viewer has LLSingleton dependency circularities. If you
// feel strongly motivated to eliminate them, cheers and good luck.
// (At that point we could consider a much simpler locking mechanism.)
@ -879,4 +880,36 @@ private: \
/* LLSINGLETON() is carefully implemented to permit exactly this */ \
LLSINGLETON_C11(DERIVED_CLASS) {}
// Relatively unsafe singleton implementation that is much faster
// and simpler than LLSingleton, but has no dependency tracking
// or inherent thread safety and requires manual invocation of
// createInstance before first use.
template<class T>
class LLSimpleton
{
public:
template <typename... ARGS>
static void createInstance(ARGS&&... args)
{
llassert(sInstance == nullptr);
sInstance = new T(std::forward<ARGS>(args)...);
}
static inline T* getInstance() { return sInstance; }
static inline T& instance() { return *getInstance(); }
static inline bool instanceExists() { return sInstance != nullptr; }
static void deleteSingleton()
{
delete sInstance;
sInstance = nullptr;
}
private:
static T* sInstance;
};
template <class T>
T* LLSimpleton<T>::sInstance{ nullptr };
#endif

View File

@ -37,9 +37,6 @@
#include <winnls.h> // for WideCharToMultiByte
#endif
LLTrace::BlockTimerStatHandle FT_STRING_FORMAT("String Format");
std::string ll_safe_string(const char* in)
{
if(in) return std::string(in);
@ -215,7 +212,7 @@ S32 utf16chars_to_wchar(const U16* inchars, llwchar* outchar)
return inchars - base;
}
llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len)
llutf16string wstring_to_utf16str(const llwchar* utf32str, size_t len)
{
llutf16string out;
@ -237,27 +234,19 @@ llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len)
return out;
}
llutf16string wstring_to_utf16str(const LLWString &utf32str)
llutf16string utf8str_to_utf16str( const char* utf8str, size_t len )
{
const S32 len = (S32)utf32str.length();
return wstring_to_utf16str(utf32str, len);
}
llutf16string utf8str_to_utf16str ( const std::string& utf8str )
{
LLWString wstr = utf8str_to_wstring ( utf8str );
LLWString wstr = utf8str_to_wstring ( utf8str, len );
return wstring_to_utf16str ( wstr );
}
LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len)
LLWString utf16str_to_wstring(const U16* utf16str, size_t len)
{
LLWString wout;
if((len <= 0) || utf16str.empty()) return wout;
if (len == 0) return wout;
S32 i = 0;
// craziness to make gcc happy (llutf16string.c_str() is tweaked on linux):
const U16* chars16 = &(*(utf16str.begin()));
const U16* chars16 = utf16str;
while (i < len)
{
llwchar cur_char;
@ -267,12 +256,6 @@ LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len)
return wout;
}
LLWString utf16str_to_wstring(const llutf16string &utf16str)
{
const S32 len = (S32)utf16str.length();
return utf16str_to_wstring(utf16str, len);
}
// Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string.
S32 utf16str_wstring_length(const llutf16string &utf16str, const S32 utf16_len)
{
@ -392,8 +375,7 @@ S32 wstring_utf8_length(const LLWString& wstr)
return len;
}
LLWString utf8str_to_wstring(const std::string& utf8str, S32 len)
LLWString utf8str_to_wstring(const char* utf8str, size_t len)
{
LLWString wout;
@ -481,13 +463,7 @@ LLWString utf8str_to_wstring(const std::string& utf8str, S32 len)
return wout;
}
LLWString utf8str_to_wstring(const std::string& utf8str)
{
const S32 len = (S32)utf8str.length();
return utf8str_to_wstring(utf8str, len);
}
std::string wstring_to_utf8str(const LLWString& utf32str, S32 len)
std::string wstring_to_utf8str(const llwchar* utf32str, size_t len)
{
std::string out;
@ -503,20 +479,9 @@ std::string wstring_to_utf8str(const LLWString& utf32str, S32 len)
return out;
}
std::string wstring_to_utf8str(const LLWString& utf32str)
std::string utf16str_to_utf8str(const U16* utf16str, size_t len)
{
const S32 len = (S32)utf32str.length();
return wstring_to_utf8str(utf32str, len);
}
std::string utf16str_to_utf8str(const llutf16string& utf16str)
{
return wstring_to_utf8str(utf16str_to_wstring(utf16str));
}
std::string utf16str_to_utf8str(const llutf16string& utf16str, S32 len)
{
return wstring_to_utf8str(utf16str_to_wstring(utf16str, len), len);
return wstring_to_utf8str(utf16str_to_wstring(utf16str, len));
}
std::string utf8str_trim(const std::string& utf8str)
@ -729,17 +694,16 @@ std::string utf8str_removeCRLF(const std::string& utf8str)
}
#if LL_WINDOWS
std::string ll_convert_wide_to_string(const wchar_t* in)
unsigned int ll_wstring_default_code_page()
{
return ll_convert_wide_to_string(in, CP_UTF8);
return CP_UTF8;
}
std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page)
std::string ll_convert_wide_to_string(const wchar_t* in, size_t len_in, unsigned int code_page)
{
std::string out;
if(in)
{
int len_in = wcslen(in);
int len_out = WideCharToMultiByte(
code_page,
0,
@ -771,12 +735,7 @@ std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page)
return out;
}
std::wstring ll_convert_string_to_wide(const std::string& in)
{
return ll_convert_string_to_wide(in, CP_UTF8);
}
std::wstring ll_convert_string_to_wide(const std::string& in, unsigned int code_page)
std::wstring ll_convert_string_to_wide(const char* in, size_t len, unsigned int code_page)
{
// From review:
// We can preallocate a wide char buffer that is the same length (in wchar_t elements) as the utf8 input,
@ -788,10 +747,10 @@ std::wstring ll_convert_string_to_wide(const std::string& in, unsigned int code_
// reserve an output buffer that will be destroyed on exit, with a place
// to put NULL terminator
std::vector<wchar_t> w_out(in.length() + 1);
std::vector<wchar_t> w_out(len + 1);
memset(&w_out[0], 0, w_out.size());
int real_output_str_len = MultiByteToWideChar(code_page, 0, in.c_str(), in.length(),
int real_output_str_len = MultiByteToWideChar(code_page, 0, in, len,
&w_out[0], w_out.size() - 1);
//looks like MultiByteToWideChar didn't add null terminator to converted string, see EXT-4858.
@ -801,30 +760,32 @@ std::wstring ll_convert_string_to_wide(const std::string& in, unsigned int code_
return {&w_out[0]};
}
LLWString ll_convert_wide_to_wstring(const std::wstring& in)
LLWString ll_convert_wide_to_wstring(const wchar_t* in, size_t len)
{
// This function, like its converse, is a placeholder, encapsulating a
// guilty little hack: the only "official" way nat has found to convert
// between std::wstring (16 bits on Windows) and LLWString (UTF-32) is
// by using iconv, which we've avoided so far. It kinda sorta works to
// just copy individual characters...
// The point is that if/when we DO introduce some more official way to
// perform such conversions, we should only have to call it here.
return { in.begin(), in.end() };
// Whether or not std::wstring and llutf16string are distinct types, they
// both hold UTF-16LE characters. (See header file comments.) Pretend this
// wchar_t* sequence is really a U16* sequence and use the conversion we
// define above.
return utf16str_to_wstring(reinterpret_cast<const U16*>(in), len);
}
std::wstring ll_convert_wstring_to_wide(const LLWString& in)
std::wstring ll_convert_wstring_to_wide(const llwchar* in, size_t len)
{
// See comments in ll_convert_wide_to_wstring()
return { in.begin(), in.end() };
// first, convert to llutf16string, for which we have a real implementation
auto utf16str{ wstring_to_utf16str(in, len) };
// then, because each U16 char must be UTF-16LE encoded, pretend the U16*
// string pointer is a wchar_t* and instantiate a std::wstring of the same
// length.
return { reinterpret_cast<const wchar_t*>(utf16str.c_str()), utf16str.length() };
}
std::string ll_convert_string_to_utf8_string(const std::string& in)
{
auto w_mesg = ll_convert_string_to_wide(in, CP_ACP);
std::string out_utf8(ll_convert_wide_to_string(w_mesg.c_str(), CP_UTF8));
return out_utf8;
// If you pass code_page, you must also pass length, otherwise the code
// page parameter will be mistaken for length.
auto w_mesg = ll_convert_string_to_wide(in, in.length(), CP_ACP);
// CP_UTF8 is default -- see ll_wstring_default_code_page() above.
return ll_convert_wide_to_string(w_mesg);
}
namespace
@ -1483,7 +1444,7 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
template<>
S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
{
LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT);
LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING;
S32 res = 0;
std::string output;
@ -1556,7 +1517,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
template<>
S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
{
LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT);
LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING;
S32 res = 0;
if (!substitutions.isMap())

View File

@ -27,9 +27,11 @@
#ifndef LL_LLSTRING_H
#define LL_LLSTRING_H
#include <boost/call_traits.hpp>
#include <boost/optional/optional.hpp>
#include <string>
#include <cstdio>
#include <cwchar> // std::wcslen()
//#include <locale>
#include <iomanip>
#include <algorithm>
@ -535,14 +537,71 @@ struct ll_convert_impl<T, T>
T operator()(const T& in) const { return in; }
};
// simple construction from char*
template<typename T>
struct ll_convert_impl<T, const typename T::value_type*>
{
T operator()(const typename T::value_type* in) const { return { in }; }
};
// specialize ll_convert_impl<TO, FROM> to return EXPR
#define ll_convert_alias(TO, FROM, EXPR) \
template<> \
struct ll_convert_impl<TO, FROM> \
{ \
TO operator()(const FROM& in) const { return EXPR; } \
/* param_type optimally passes both char* and string */ \
TO operator()(typename boost::call_traits<FROM>::param_type in) const { return EXPR; } \
}
// If all we're doing is copying characters, pass this to ll_convert_alias as
// EXPR. Since it expands into the 'return EXPR' slot in the ll_convert_impl
// specialization above, it implies TO{ in.begin(), in.end() }.
#define LL_CONVERT_COPY_CHARS { in.begin(), in.end() }
// Generic name for strlen() / wcslen() - the default implementation should
// (!) work with U16 and llwchar, but we don't intend to engage it.
template <typename CHARTYPE>
size_t ll_convert_length(const CHARTYPE* zstr)
{
const CHARTYPE* zp;
// classic C string scan
for (zp = zstr; *zp; ++zp)
;
return (zp - zstr);
}
// specialize where we have a library function; may use intrinsic operations
template <>
inline size_t ll_convert_length<wchar_t>(const wchar_t* zstr) { return std::wcslen(zstr); }
template <>
inline size_t ll_convert_length<char> (const char* zstr) { return std::strlen(zstr); }
// ll_convert_forms() is short for a bunch of boilerplate. It defines
// longname(const char*, len), longname(const char*), longname(const string&)
// and longname(const string&, len) so calls written pre-ll_convert() will
// work. Most of these overloads will be unified once we turn on C++17 and can
// use std::string_view.
// It also uses aliasmacro to ensure that both ll_convert<OUTSTR>(const char*)
// and ll_convert<OUTSTR>(const string&) will work.
#define ll_convert_forms(aliasmacro, OUTSTR, INSTR, longname) \
LL_COMMON_API OUTSTR longname(const INSTR::value_type* in, size_t len); \
inline auto longname(const INSTR& in, size_t len) \
{ \
return longname(in.c_str(), len); \
} \
inline auto longname(const INSTR::value_type* in) \
{ \
return longname(in, ll_convert_length(in)); \
} \
inline auto longname(const INSTR& in) \
{ \
return longname(in.c_str(), in.length()); \
} \
/* string param */ \
aliasmacro(OUTSTR, INSTR, longname(in)); \
/* char* param */ \
aliasmacro(OUTSTR, const INSTR::value_type*, longname(in))
// Make the incoming string a utf8 string. Replaces any unknown glyph
// with the UNKNOWN_CHARACTER. Once any unknown glyph is found, the rest
// of the data may not be recovered.
@ -579,63 +638,47 @@ LL_COMMON_API std::string rawstr_to_utf8(const std::string& raw);
// LL_WCHAR_T_NATIVE.
typedef std::basic_string<U16> llutf16string;
#if ! defined(LL_WCHAR_T_NATIVE)
// wchar_t is identical to U16, and std::wstring is identical to llutf16string.
// Defining an ll_convert alias involving llutf16string would collide with the
// comparable preferred alias involving std::wstring. (In this scenario, if
// you pass llutf16string, it will engage the std::wstring specialization.)
#define ll_convert_u16_alias(TO, FROM, EXPR) // nothing
#else // defined(LL_WCHAR_T_NATIVE)
// wchar_t is a distinct native type, so llutf16string is also a distinct
// type, and there IS a point to converting separately to/from llutf16string.
// (But why? Windows APIs are still defined in terms of wchar_t, and
// in this scenario llutf16string won't work for them!)
#define ll_convert_u16_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR)
// Considering wchar_t, llwchar and U16, there are three relevant cases:
#if LLWCHAR_IS_WCHAR_T // every which way but Windows
// llwchar is identical to wchar_t, LLWString is identical to std::wstring.
// U16 is distinct, llutf16string is distinct (though pretty useless).
// Given conversions to/from LLWString and to/from llutf16string, conversions
// involving std::wstring would collide.
#define ll_convert_wstr_alias(TO, FROM, EXPR) // nothing
// but we can define conversions involving llutf16string without collisions
#define ll_convert_u16_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR)
#if LL_WINDOWS
// LL_WCHAR_T_NATIVE is defined on non-Windows systems because, in fact,
// wchar_t is native. Everywhere but Windows, we use it for llwchar (see
// stdtypes.h). That makes LLWString identical to std::wstring, so these
// aliases for std::wstring would collide with those for LLWString. Only
// define on Windows, where converting between std::wstring and llutf16string
// means copying chars.
ll_convert_alias(llutf16string, std::wstring, llutf16string(in.begin(), in.end()));
ll_convert_alias(std::wstring, llutf16string, std::wstring(in.begin(), in.end()));
#endif // LL_WINDOWS
#endif // defined(LL_WCHAR_T_NATIVE)
#elif defined(LL_WCHAR_T_NATIVE) // Windows, either clang or MS /Zc:wchar_t
// llwchar (32-bit), wchar_t (16-bit) and U16 are all different types.
// Conversions to/from LLWString, to/from std::wstring and to/from llutf16string
// can all be defined.
#define ll_convert_wstr_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR)
#define ll_convert_u16_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR)
LL_COMMON_API LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len);
LL_COMMON_API LLWString utf16str_to_wstring(const llutf16string &utf16str);
ll_convert_u16_alias(LLWString, llutf16string, utf16str_to_wstring(in));
#else // ! LL_WCHAR_T_NATIVE: Windows with MS /Zc:wchar_t-
// wchar_t is identical to U16, std::wstring is identical to llutf16string.
// Given conversions to/from LLWString and to/from std::wstring, conversions
// involving llutf16string would collide.
#define ll_convert_u16_alias(TO, FROM, EXPR) // nothing
// but we can define conversions involving std::wstring without collisions
#define ll_convert_wstr_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR)
#endif
LL_COMMON_API llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len);
LL_COMMON_API llutf16string wstring_to_utf16str(const LLWString &utf32str);
ll_convert_u16_alias(llutf16string, LLWString, wstring_to_utf16str(in));
ll_convert_forms(ll_convert_u16_alias, LLWString, llutf16string, utf16str_to_wstring);
ll_convert_forms(ll_convert_u16_alias, llutf16string, LLWString, wstring_to_utf16str);
ll_convert_forms(ll_convert_u16_alias, llutf16string, std::string, utf8str_to_utf16str);
ll_convert_forms(ll_convert_alias, LLWString, std::string, utf8str_to_wstring);
LL_COMMON_API llutf16string utf8str_to_utf16str ( const std::string& utf8str, S32 len);
LL_COMMON_API llutf16string utf8str_to_utf16str ( const std::string& utf8str );
ll_convert_u16_alias(llutf16string, std::string, utf8str_to_utf16str(in));
LL_COMMON_API LLWString utf8str_to_wstring(const std::string &utf8str, S32 len);
LL_COMMON_API LLWString utf8str_to_wstring(const std::string &utf8str);
// Same function, better name. JC
inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); }
// best name of all
ll_convert_alias(LLWString, std::string, utf8string_to_wstring(in));
//
LL_COMMON_API S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str);
ll_convert_alias(std::string, LLWString, wstring_to_utf8str(in));
LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str);
ll_convert_u16_alias(std::string, llutf16string, utf16str_to_utf8str(in));
ll_convert_forms(ll_convert_alias, std::string, LLWString, wstring_to_utf8str);
ll_convert_forms(ll_convert_u16_alias, std::string, llutf16string, utf16str_to_utf8str);
#if LL_WINDOWS
// an older alias for utf16str_to_utf8str(llutf16string)
inline std::string wstring_to_utf8str(const llutf16string &utf16str) { return utf16str_to_utf8str(utf16str);}
#endif
// Length of this UTF32 string in bytes when transformed to UTF8
LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr);
@ -714,42 +757,48 @@ LL_COMMON_API std::string utf8str_removeCRLF(const std::string& utf8str);
//@{
/**
* @brief Convert a wide string to std::string
* @brief Convert a wide string to/from std::string
* Convert a Windows wide string to/from our LLWString
*
* This replaces the unsafe W2A macro from ATL.
*/
LL_COMMON_API std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page);
LL_COMMON_API std::string ll_convert_wide_to_string(const wchar_t* in); // default CP_UTF8
inline std::string ll_convert_wide_to_string(const std::wstring& in, unsigned int code_page)
{
return ll_convert_wide_to_string(in.c_str(), code_page);
}
inline std::string ll_convert_wide_to_string(const std::wstring& in)
{
return ll_convert_wide_to_string(in.c_str());
}
ll_convert_alias(std::string, std::wstring, ll_convert_wide_to_string(in));
// Avoid requiring this header to #include the Windows header file declaring
// our actual default code_page by delegating this function to our .cpp file.
LL_COMMON_API unsigned int ll_wstring_default_code_page();
/**
* Converts a string to wide string.
*/
LL_COMMON_API std::wstring ll_convert_string_to_wide(const std::string& in,
unsigned int code_page);
LL_COMMON_API std::wstring ll_convert_string_to_wide(const std::string& in);
// default CP_UTF8
ll_convert_alias(std::wstring, std::string, ll_convert_string_to_wide(in));
// This is like ll_convert_forms(), with the added complexity of a code page
// parameter that may or may not be passed.
#define ll_convert_cp_forms(aliasmacro, OUTSTR, INSTR, longname) \
/* declare the only nontrivial implementation (in .cpp file) */ \
LL_COMMON_API OUTSTR longname( \
const INSTR::value_type* in, \
size_t len, \
unsigned int code_page=ll_wstring_default_code_page()); \
/* if passed only a char pointer, scan for nul terminator */ \
inline auto longname(const INSTR::value_type* in) \
{ \
return longname(in, ll_convert_length(in)); \
} \
/* if passed string and length, extract its char pointer */ \
inline auto longname( \
const INSTR& in, \
size_t len, \
unsigned int code_page=ll_wstring_default_code_page()) \
{ \
return longname(in.c_str(), len, code_page); \
} \
/* if passed only a string object, no scan, pass known length */ \
inline auto longname(const INSTR& in) \
{ \
return longname(in.c_str(), in.length()); \
} \
aliasmacro(OUTSTR, INSTR, longname(in)); \
aliasmacro(OUTSTR, const INSTR::value_type*, longname(in))
/**
* Convert a Windows wide string to our LLWString
*/
LL_COMMON_API LLWString ll_convert_wide_to_wstring(const std::wstring& in);
ll_convert_alias(LLWString, std::wstring, ll_convert_wide_to_wstring(in));
/**
* Convert LLWString to Windows wide string
*/
LL_COMMON_API std::wstring ll_convert_wstring_to_wide(const LLWString& in);
ll_convert_alias(std::wstring, LLWString, ll_convert_wstring_to_wide(in));
ll_convert_cp_forms(ll_convert_wstr_alias, std::string, std::wstring, ll_convert_wide_to_string);
ll_convert_cp_forms(ll_convert_wstr_alias, std::wstring, std::string, ll_convert_string_to_wide);
ll_convert_forms(ll_convert_wstr_alias, LLWString, std::wstring, ll_convert_wide_to_wstring);
ll_convert_forms(ll_convert_wstr_alias, std::wstring, LLWString, ll_convert_wstring_to_wide);
/**
* Converts incoming string into utf8 string
@ -1950,4 +1999,14 @@ void LLStringUtilBase<T>::truncate(string_type& string, size_type count)
string.resize(count < cur_size ? count : cur_size);
}
// The good thing about *declaration* macros, vs. usage macros, is that now
// we're done with them: we don't need them to bleed into the consuming source
// file.
#undef ll_convert_alias
#undef ll_convert_u16_alias
#undef ll_convert_wstr_alias
#undef LL_CONVERT_COPY_CHARS
#undef ll_convert_forms
#undef ll_convert_cp_forms
#endif // LL_STRING_H

View File

@ -881,6 +881,7 @@ LLSD LLMemoryInfo::getStatsMap() const
LLMemoryInfo& LLMemoryInfo::refresh()
{
LL_PROFILE_ZONE_SCOPED
mStatsMap = loadStatsMap();
LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n";
@ -890,11 +891,9 @@ LLMemoryInfo& LLMemoryInfo::refresh()
return *this;
}
static LLTrace::BlockTimerStatHandle FTM_MEMINFO_LOAD_STATS("MemInfo Load Stats");
LLSD LLMemoryInfo::loadStatsMap()
{
LL_RECORD_BLOCK_TIME(FTM_MEMINFO_LOAD_STATS);
LL_PROFILE_ZONE_SCOPED;
// This implementation is derived from stream() code (as of 2011-06-29).
Stats stats;

View File

@ -35,7 +35,6 @@
#include "lltrace.h"
#include "lltracethreadrecorder.h"
#include "llexception.h"
#include "fstelemetry.h" // <FS:Beq> allow thread naming
#if LL_LINUX
#include <sched.h>
@ -136,6 +135,8 @@ void LLThread::threadRun()
set_thread_name(-1, mName.c_str());
#endif
LL_PROFILER_SET_THREAD_NAME( mName.c_str() );
// this is the first point at which we're actually running in the new thread
mID = currentID();
@ -143,14 +144,16 @@ void LLThread::threadRun()
mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder());
// <FS:Beq> - Add threadnames to telemetry
LL_INFOS("THREAD") << "Started thread " << mName << LL_ENDL;
FSThreadName( mName.c_str() );
LL_PROFILER_SET_THREAD_NAME( mName.c_str() );
// </FS:Beq>
// Run the user supplied function
do
{
try
{
LL_PROFILER_THREAD_BEGIN(mName.c_str())
run();
LL_PROFILER_THREAD_END(mName.c_str())
}
catch (const LLContinueError &e)
{
@ -335,6 +338,8 @@ bool LLThread::runCondition(void)
// Stop thread execution if requested until unpaused.
void LLThread::checkPause()
{
LL_PROFILER_THREAD_BEGIN(mName.c_str())
mDataLock->lock();
// This is in a while loop because the pthread API allows for spurious wakeups.
@ -347,6 +352,8 @@ void LLThread::checkPause()
}
mDataLock->unlock();
LL_PROFILER_THREAD_END(mName.c_str())
}
//============================================================================
@ -367,27 +374,34 @@ void LLThread::setQuitting()
// <FS:Beq> give this a better chance to inline
// LLThread::id_t LLThread::currentID()
// {
// LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
// return std::this_thread::get_id();
// }
// static
void LLThread::yield()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
std::this_thread::yield();
}
void LLThread::wake()
{
LL_PROFILER_THREAD_BEGIN(mName.c_str())
mDataLock->lock();
if(!shouldSleep())
{
mRunCondition->signal();
}
mDataLock->unlock();
LL_PROFILER_THREAD_END(mName.c_str())
}
void LLThread::wakeLocked()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
if(!shouldSleep())
{
mRunCondition->signal();
@ -396,11 +410,13 @@ void LLThread::wakeLocked()
void LLThread::lockData()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
mDataLock->lock();
}
void LLThread::unlockData()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
mDataLock->unlock();
}

View File

@ -1,6 +1,6 @@
/**
* @file llthreadsafequeue.h
* @brief Base classes for thread, mutex and condition handling.
* @brief Queue protected with mutexes for cross-thread use
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
@ -27,16 +27,19 @@
#ifndef LL_LLTHREADSAFEQUEUE_H
#define LL_LLTHREADSAFEQUEUE_H
#include "llexception.h"
#include <deque>
#include <string>
#include <chrono>
#include "mutex.h"
#include "llcoros.h"
#include LLCOROS_MUTEX_HEADER
#include <boost/fiber/timed_mutex.hpp>
#include LLCOROS_CONDVAR_HEADER
#include "llexception.h"
#include "mutex.h"
#include <chrono>
#include <queue>
#include <string>
/*****************************************************************************
* LLThreadSafeQueue
*****************************************************************************/
//
// A general queue exception.
//
@ -66,70 +69,116 @@ public:
}
};
//
// Implements a thread safe FIFO.
//
template<typename ElementT>
/**
* Implements a thread safe FIFO.
*/
// Let the default std::queue default to underlying std::deque. Override if
// desired.
template<typename ElementT, typename QueueT=std::queue<ElementT>>
class LLThreadSafeQueue
{
public:
typedef ElementT value_type;
// If the pool is set to NULL one will be allocated and managed by this
// queue.
// Limiting the number of pending items prevents unbounded growth of the
// underlying queue.
LLThreadSafeQueue(U32 capacity = 1024);
// Add an element to the front of queue (will block if the queue has
// reached capacity).
virtual ~LLThreadSafeQueue() {}
// Add an element to the queue (will block if the queue has reached
// capacity).
//
// This call will raise an interrupt error if the queue is closed while
// the caller is blocked.
void pushFront(ElementT const & element);
// Try to add an element to the front of queue without blocking. Returns
// true only if the element was actually added.
bool tryPushFront(ElementT const & element);
template <typename T>
void push(T&& element);
// legacy name
void pushFront(ElementT const & element) { return push(element); }
// Try to add an element to the front of queue, blocking if full but with
// timeout. Returns true if the element was added.
// Add an element to the queue (will block if the queue has reached
// capacity). Return false if the queue is closed before push is possible.
template <typename T>
bool pushIfOpen(T&& element);
// Try to add an element to the queue without blocking. Returns
// true only if the element was actually added.
template <typename T>
bool tryPush(T&& element);
// legacy name
bool tryPushFront(ElementT const & element) { return tryPush(element); }
// Try to add an element to the queue, blocking if full but with timeout
// after specified duration. Returns true if the element was added.
// There are potentially two different timeouts involved: how long to try
// to lock the mutex, versus how long to wait for the queue to stop being
// full. Careful settings for each timeout might be orders of magnitude
// apart. However, this method conflates them.
template <typename Rep, typename Period, typename T>
bool tryPushFor(const std::chrono::duration<Rep, Period>& timeout,
T&& element);
// legacy name
template <typename Rep, typename Period>
bool tryPushFrontFor(const std::chrono::duration<Rep, Period>& timeout,
ElementT const & element);
ElementT const & element) { return tryPushFor(timeout, element); }
// Pop the element at the end of the queue (will block if the queue is
// Try to add an element to the queue, blocking if full but with
// timeout at specified time_point. Returns true if the element was added.
template <typename Clock, typename Duration, typename T>
bool tryPushUntil(const std::chrono::time_point<Clock, Duration>& until,
T&& element);
// no legacy name because this is a newer method
// Pop the element at the head of the queue (will block if the queue is
// empty).
//
// This call will raise an interrupt error if the queue is closed while
// the caller is blocked.
ElementT popBack(void);
// Pop an element from the end of the queue if there is one available.
ElementT pop(void);
// legacy name
ElementT popBack(void) { return pop(); }
// Pop an element from the head of the queue if there is one available.
// Returns true only if an element was popped.
bool tryPopBack(ElementT & element);
bool tryPop(ElementT & element);
// legacy name
bool tryPopBack(ElementT & element) { return tryPop(element); }
// Pop the element at the head of the queue, blocking if empty, with
// timeout after specified duration. Returns true if an element was popped.
template <typename Rep, typename Period>
bool tryPopFor(const std::chrono::duration<Rep, Period>& timeout, ElementT& element);
// no legacy name because this is a newer method
// Pop the element at the head of the queue, blocking if empty, with
// timeout at specified time_point. Returns true if an element was popped.
template <typename Clock, typename Duration>
bool tryPopUntil(const std::chrono::time_point<Clock, Duration>& until,
ElementT& element);
// no legacy name because this is a newer method
// Returns the size of the queue.
size_t size();
//Returns the capacity of the queue.
U32 capacity() { return mCapacity; }
// closes the queue:
// - every subsequent pushFront() call will throw LLThreadSafeQueueInterrupt
// - every subsequent tryPushFront() call will return false
// - popBack() calls will return normally until the queue is drained, then
// every subsequent popBack() will throw LLThreadSafeQueueInterrupt
// - tryPopBack() calls will return normally until the queue is drained,
// then every subsequent tryPopBack() call will return false
// - every subsequent push() call will throw LLThreadSafeQueueInterrupt
// - every subsequent tryPush() call will return false
// - pop() calls will return normally until the queue is drained, then
// every subsequent pop() will throw LLThreadSafeQueueInterrupt
// - tryPop() calls will return normally until the queue is drained,
// then every subsequent tryPop() call will return false
void close();
// detect closed state
// producer end: are we prevented from pushing any additional items?
bool isClosed();
// inverse of isClosed()
explicit operator bool();
// consumer end: are we done, is the queue entirely drained?
bool done();
private:
std::deque< ElementT > mStorage;
protected:
typedef QueueT queue_type;
QueueT mStorage;
U32 mCapacity;
bool mClosed;
@ -137,37 +186,154 @@ private:
typedef std::unique_lock<decltype(mLock)> lock_t;
boost::fibers::condition_variable_any mCapacityCond;
boost::fibers::condition_variable_any mEmptyCond;
enum pop_result { EMPTY, DONE, WAITING, POPPED };
// implementation logic, suitable for passing to tryLockUntil()
template <typename Clock, typename Duration>
pop_result tryPopUntil_(lock_t& lock,
const std::chrono::time_point<Clock, Duration>& until,
ElementT& element);
// if we're able to lock immediately, do so and run the passed callable,
// which must accept lock_t& and return bool
template <typename CALLABLE>
bool tryLock(CALLABLE&& callable);
// if we're able to lock before the passed time_point, do so and run the
// passed callable, which must accept lock_t& and return bool
template <typename Clock, typename Duration, typename CALLABLE>
bool tryLockUntil(const std::chrono::time_point<Clock, Duration>& until,
CALLABLE&& callable);
// while lock is locked, really push the passed element, if we can
template <typename T>
bool push_(lock_t& lock, T&& element);
// while lock is locked, really pop the head element, if we can
pop_result pop_(lock_t& lock, ElementT& element);
// Is the current head element ready to pop? We say yes; subclass can
// override as needed.
virtual bool canPop(const ElementT& head) const { return true; }
};
// LLThreadSafeQueue
//-----------------------------------------------------------------------------
/*****************************************************************************
* PriorityQueueAdapter
*****************************************************************************/
namespace LL
{
/**
* std::priority_queue's API is almost like std::queue, intentionally of
* course, but you must access the element about to pop() as top() rather
* than as front(). Make an adapter for use with LLThreadSafeQueue.
*/
template <typename T, typename Container=std::vector<T>,
typename Compare=std::less<typename Container::value_type>>
class PriorityQueueAdapter
{
public:
// publish all the same types
typedef std::priority_queue<T, Container, Compare> queue_type;
typedef typename queue_type::container_type container_type;
typedef typename queue_type::value_compare value_compare;
typedef typename queue_type::value_type value_type;
typedef typename queue_type::size_type size_type;
typedef typename queue_type::reference reference;
typedef typename queue_type::const_reference const_reference;
template<typename ElementT>
LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(U32 capacity) :
// Although std::queue defines both const and non-const front()
// methods, std::priority_queue defines only const top().
const_reference front() const { return mQ.top(); }
// std::priority_queue has no equivalent to back(), so it's good that
// LLThreadSafeQueue doesn't use it.
// All the rest of these merely forward to the corresponding
// queue_type methods.
bool empty() const { return mQ.empty(); }
size_type size() const { return mQ.size(); }
void push(const value_type& value) { mQ.push(value); }
void push(value_type&& value) { mQ.push(std::move(value)); }
template <typename... Args>
void emplace(Args&&... args) { mQ.emplace(std::forward<Args>(args)...); }
void pop() { mQ.pop(); }
private:
queue_type mQ;
};
} // namespace LL
/*****************************************************************************
* LLThreadSafeQueue implementation
*****************************************************************************/
template<typename ElementT, typename QueueT>
LLThreadSafeQueue<ElementT, QueueT>::LLThreadSafeQueue(U32 capacity) :
mCapacity(capacity),
mClosed(false)
{
}
template<typename ElementT>
void LLThreadSafeQueue<ElementT>::pushFront(ElementT const & element)
// if we're able to lock immediately, do so and run the passed callable, which
// must accept lock_t& and return bool
template <typename ElementT, typename QueueT>
template <typename CALLABLE>
bool LLThreadSafeQueue<ElementT, QueueT>::tryLock(CALLABLE&& callable)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock())
return false;
return std::forward<CALLABLE>(callable)(lock1);
}
// if we're able to lock before the passed time_point, do so and run the
// passed callable, which must accept lock_t& and return bool
template <typename ElementT, typename QueueT>
template <typename Clock, typename Duration, typename CALLABLE>
bool LLThreadSafeQueue<ElementT, QueueT>::tryLockUntil(
const std::chrono::time_point<Clock, Duration>& until,
CALLABLE&& callable)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock_until(until))
return false;
return std::forward<CALLABLE>(callable)(lock1);
}
// while lock is locked, really push the passed element, if we can
template <typename ElementT, typename QueueT>
template <typename T>
bool LLThreadSafeQueue<ElementT, QueueT>::push_(lock_t& lock, T&& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
if (mStorage.size() >= mCapacity)
return false;
mStorage.push(std::forward<T>(element));
lock.unlock();
// now that we've pushed, if somebody's been waiting to pop, signal them
mEmptyCond.notify_one();
return true;
}
template <typename ElementT, typename QueueT>
template <typename T>
bool LLThreadSafeQueue<ElementT, QueueT>::pushIfOpen(T&& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lock_t lock1(mLock);
while (true)
{
// On the producer side, it doesn't matter whether the queue has been
// drained or not: the moment either end calls close(), further push()
// operations will fail.
if (mClosed)
{
LLTHROW(LLThreadSafeQueueInterrupt());
}
return false;
if (mStorage.size() < mCapacity)
{
mStorage.push_front(element);
lock1.unlock();
mEmptyCond.notify_one();
return;
}
if (push_(lock1, std::forward<T>(element)))
return true;
// Storage Full. Wait for signal.
mCapacityCond.wait(lock1);
@ -175,39 +341,205 @@ void LLThreadSafeQueue<ElementT>::pushFront(ElementT const & element)
}
template <typename ElementT>
template <typename Rep, typename Period>
bool LLThreadSafeQueue<ElementT>::tryPushFrontFor(const std::chrono::duration<Rep, Period>& timeout,
ElementT const & element)
template <typename ElementT, typename QueueT>
template<typename T>
void LLThreadSafeQueue<ElementT, QueueT>::push(T&& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
if (! pushIfOpen(std::forward<T>(element)))
{
LLTHROW(LLThreadSafeQueueInterrupt());
}
}
template<typename ElementT, typename QueueT>
template<typename T>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPush(T&& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
return tryLock(
[this, element=std::move(element)](lock_t& lock)
{
if (mClosed)
return false;
return push_(lock, std::move(element));
});
}
template <typename ElementT, typename QueueT>
template <typename Rep, typename Period, typename T>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPushFor(
const std::chrono::duration<Rep, Period>& timeout,
T&& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
// Convert duration to time_point: passing the same timeout duration to
// each of multiple calls is wrong.
auto endpoint = std::chrono::steady_clock::now() + timeout;
return tryPushUntil(std::chrono::steady_clock::now() + timeout,
std::forward<T>(element));
}
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock_until(endpoint))
return false;
template <typename ElementT, typename QueueT>
template <typename Clock, typename Duration, typename T>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPushUntil(
const std::chrono::time_point<Clock, Duration>& until,
T&& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
return tryLockUntil(
until,
[this, until, element=std::move(element)](lock_t& lock)
{
while (true)
{
if (mClosed)
{
return false;
}
if (push_(lock, std::move(element)))
return true;
// Storage Full. Wait for signal.
if (LLCoros::cv_status::timeout == mCapacityCond.wait_until(lock, until))
{
// timed out -- formally we might recheck both conditions above
return false;
}
// If we didn't time out, we were notified for some reason. Loop back
// to check.
}
});
}
// while lock is locked, really pop the head element, if we can
template <typename ElementT, typename QueueT>
typename LLThreadSafeQueue<ElementT, QueueT>::pop_result
LLThreadSafeQueue<ElementT, QueueT>::pop_(lock_t& lock, ElementT& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
// If mStorage is empty, there's no head element.
if (mStorage.empty())
return mClosed? DONE : EMPTY;
// If there's a head element, pass it to canPop() to see if it's ready to pop.
if (! canPop(mStorage.front()))
return WAITING;
// std::queue::front() is the element about to pop()
element = mStorage.front();
mStorage.pop();
lock.unlock();
// now that we've popped, if somebody's been waiting to push, signal them
mCapacityCond.notify_one();
return POPPED;
}
template<typename ElementT, typename QueueT>
ElementT LLThreadSafeQueue<ElementT, QueueT>::pop(void)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lock_t lock1(mLock);
ElementT value;
while (true)
{
if (mClosed)
// On the consumer side, we always try to pop before checking mClosed
// so we can finish draining the queue.
pop_result popped = pop_(lock1, value);
if (popped == POPPED)
return std::move(value);
// Once the queue is DONE, there will never be any more coming.
if (popped == DONE)
{
return false;
LLTHROW(LLThreadSafeQueueInterrupt());
}
if (mStorage.size() < mCapacity)
// If we didn't pop because WAITING, i.e. canPop() returned false,
// then even if the producer end has been closed, there's still at
// least one item to drain: wait for it. Or we might be EMPTY, with
// the queue still open. Either way, wait for signal.
mEmptyCond.wait(lock1);
}
}
template<typename ElementT, typename QueueT>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPop(ElementT & element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
return tryLock(
[this, &element](lock_t& lock)
{
mStorage.push_front(element);
lock1.unlock();
mEmptyCond.notify_one();
return true;
// conflate EMPTY, DONE, WAITING: tryPop() behavior when the queue
// is closed is implemented by simple inability to push any new
// elements
return pop_(lock, element) == POPPED;
});
}
template <typename ElementT, typename QueueT>
template <typename Rep, typename Period>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPopFor(
const std::chrono::duration<Rep, Period>& timeout,
ElementT& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
// Convert duration to time_point: passing the same timeout duration to
// each of multiple calls is wrong.
return tryPopUntil(std::chrono::steady_clock::now() + timeout, element);
}
template <typename ElementT, typename QueueT>
template <typename Clock, typename Duration>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPopUntil(
const std::chrono::time_point<Clock, Duration>& until,
ElementT& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
return tryLockUntil(
until,
[this, until, &element](lock_t& lock)
{
// conflate EMPTY, DONE, WAITING
return tryPopUntil_(lock, until, element) == POPPED;
});
}
// body of tryPopUntil(), called once we have the lock
template <typename ElementT, typename QueueT>
template <typename Clock, typename Duration>
typename LLThreadSafeQueue<ElementT, QueueT>::pop_result
LLThreadSafeQueue<ElementT, QueueT>::tryPopUntil_(
lock_t& lock,
const std::chrono::time_point<Clock, Duration>& until,
ElementT& element)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
while (true)
{
pop_result popped = pop_(lock, element);
if (popped == POPPED || popped == DONE)
{
// If we succeeded, great! If we've drained the last item, so be
// it. Either way, break the loop and tell caller.
return popped;
}
// Storage Full. Wait for signal.
if (LLCoros::cv_status::timeout == mCapacityCond.wait_until(lock1, endpoint))
// EMPTY or WAITING: wait for signal.
if (LLCoros::cv_status::timeout == mEmptyCond.wait_until(lock, until))
{
// timed out -- formally we might recheck both conditions above
return false;
// timed out -- formally we might recheck
// as it is, break loop
return popped;
}
// If we didn't time out, we were notified for some reason. Loop back
// to check.
@ -215,102 +547,44 @@ bool LLThreadSafeQueue<ElementT>::tryPushFrontFor(const std::chrono::duration<Re
}
template<typename ElementT>
bool LLThreadSafeQueue<ElementT>::tryPushFront(ElementT const & element)
{
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock())
return false;
if (mClosed)
return false;
if (mStorage.size() >= mCapacity)
return false;
mStorage.push_front(element);
lock1.unlock();
mEmptyCond.notify_one();
return true;
}
template<typename ElementT>
ElementT LLThreadSafeQueue<ElementT>::popBack(void)
{
lock_t lock1(mLock);
while (true)
{
if (!mStorage.empty())
{
ElementT value = mStorage.back();
mStorage.pop_back();
lock1.unlock();
mCapacityCond.notify_one();
return value;
}
if (mClosed)
{
LLTHROW(LLThreadSafeQueueInterrupt());
}
// Storage empty. Wait for signal.
mEmptyCond.wait(lock1);
}
}
template<typename ElementT>
bool LLThreadSafeQueue<ElementT>::tryPopBack(ElementT & element)
{
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock())
return false;
// no need to check mClosed: tryPopBack() behavior when the queue is
// closed is implemented by simple inability to push any new elements
if (mStorage.empty())
return false;
element = mStorage.back();
mStorage.pop_back();
lock1.unlock();
mCapacityCond.notify_one();
return true;
}
template<typename ElementT>
size_t LLThreadSafeQueue<ElementT>::size(void)
template<typename ElementT, typename QueueT>
size_t LLThreadSafeQueue<ElementT, QueueT>::size(void)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lock_t lock(mLock);
return mStorage.size();
}
template<typename ElementT>
void LLThreadSafeQueue<ElementT>::close()
template<typename ElementT, typename QueueT>
void LLThreadSafeQueue<ElementT, QueueT>::close()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lock_t lock(mLock);
mClosed = true;
lock.unlock();
// wake up any blocked popBack() calls
// wake up any blocked pop() calls
mEmptyCond.notify_all();
// wake up any blocked pushFront() calls
// wake up any blocked push() calls
mCapacityCond.notify_all();
}
template<typename ElementT>
bool LLThreadSafeQueue<ElementT>::isClosed()
template<typename ElementT, typename QueueT>
bool LLThreadSafeQueue<ElementT, QueueT>::isClosed()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lock_t lock(mLock);
return mClosed && mStorage.size() == 0;
return mClosed;
}
template<typename ElementT>
LLThreadSafeQueue<ElementT>::operator bool()
template<typename ElementT, typename QueueT>
bool LLThreadSafeQueue<ElementT, QueueT>::done()
{
return ! isClosed();
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lock_t lock(mLock);
return mClosed && mStorage.empty();
}
#endif

View File

@ -61,6 +61,7 @@ TimeBlockTreeNode::TimeBlockTreeNode()
void TimeBlockTreeNode::setParent( BlockTimerStatHandle* parent )
{
LL_PROFILE_ZONE_SCOPED;
llassert_always(parent != mBlock);
llassert_always(parent != NULL);

View File

@ -245,6 +245,7 @@ public:
void setName(const char* name)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mName = name;
setKey(name);
}
@ -252,12 +253,14 @@ public:
/*virtual*/ const char* getUnitLabel() const { return "KB"; }
StatType<MemAccumulator::AllocationFacet>& allocations()
{
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return static_cast<StatType<MemAccumulator::AllocationFacet>&>(*(StatType<MemAccumulator>*)this);
}
StatType<MemAccumulator::DeallocationFacet>& deallocations()
{
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return static_cast<StatType<MemAccumulator::DeallocationFacet>&>(*(StatType<MemAccumulator>*)this);
}
};
@ -279,6 +282,7 @@ struct MeasureMem<T, typename T::mem_trackable_tag_t, IS_BYTES>
{
static size_t measureFootprint(const T& value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return sizeof(T) + value.getMemFootprint();
}
};
@ -288,6 +292,7 @@ struct MeasureMem<T, IS_MEM_TRACKABLE, typename T::is_unit_t>
{
static size_t measureFootprint(const T& value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return U32Bytes(value).value();
}
};
@ -297,6 +302,7 @@ struct MeasureMem<T*, IS_MEM_TRACKABLE, IS_BYTES>
{
static size_t measureFootprint(const T* value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
if (!value)
{
return 0;
@ -341,6 +347,7 @@ struct MeasureMem<std::basic_string<T>, IS_MEM_TRACKABLE, IS_BYTES>
{
static size_t measureFootprint(const std::basic_string<T>& value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return value.capacity() * sizeof(T);
}
};
@ -349,6 +356,7 @@ struct MeasureMem<std::basic_string<T>, IS_MEM_TRACKABLE, IS_BYTES>
template<typename T>
inline void claim_alloc(MemStatHandle& measurement, const T& value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
#if LL_TRACE_ENABLED
S32 size = MeasureMem<T>::measureFootprint(value);
if(size == 0) return;
@ -361,6 +369,7 @@ inline void claim_alloc(MemStatHandle& measurement, const T& value)
template<typename T>
inline void disclaim_alloc(MemStatHandle& measurement, const T& value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
#if LL_TRACE_ENABLED
S32 size = MeasureMem<T>::measureFootprint(value);
if(size == 0) return;
@ -370,141 +379,6 @@ inline void disclaim_alloc(MemStatHandle& measurement, const T& value)
#endif
}
template<typename DERIVED, size_t ALIGNMENT = LL_DEFAULT_HEAP_ALIGN>
class MemTrackableNonVirtual
{
public:
typedef void mem_trackable_tag_t;
MemTrackableNonVirtual(const char* name)
#if LL_TRACE_ENABLED
: mMemFootprint(0)
#endif
{
#if LL_TRACE_ENABLED
static bool name_initialized = false;
if (!name_initialized)
{
name_initialized = true;
sMemStat.setName(name);
}
#endif
}
#if LL_TRACE_ENABLED
~MemTrackableNonVirtual()
{
disclaimMem(mMemFootprint);
}
static MemStatHandle& getMemStatHandle()
{
return sMemStat;
}
S32 getMemFootprint() const { return mMemFootprint; }
#endif
void* operator new(size_t size)
{
#if LL_TRACE_ENABLED
claim_alloc(sMemStat, size);
#endif
return ll_aligned_malloc<ALIGNMENT>(size);
}
template<int CUSTOM_ALIGNMENT>
static void* aligned_new(size_t size)
{
#if LL_TRACE_ENABLED
claim_alloc(sMemStat, size);
#endif
return ll_aligned_malloc<CUSTOM_ALIGNMENT>(size);
}
void operator delete(void* ptr, size_t size)
{
#if LL_TRACE_ENABLED
disclaim_alloc(sMemStat, size);
#endif
ll_aligned_free<ALIGNMENT>(ptr);
}
template<int CUSTOM_ALIGNMENT>
static void aligned_delete(void* ptr, size_t size)
{
#if LL_TRACE_ENABLED
disclaim_alloc(sMemStat, size);
#endif
ll_aligned_free<CUSTOM_ALIGNMENT>(ptr);
}
void* operator new [](size_t size)
{
#if LL_TRACE_ENABLED
claim_alloc(sMemStat, size);
#endif
return ll_aligned_malloc<ALIGNMENT>(size);
}
void operator delete[](void* ptr, size_t size)
{
#if LL_TRACE_ENABLED
disclaim_alloc(sMemStat, size);
#endif
ll_aligned_free<ALIGNMENT>(ptr);
}
// claim memory associated with other objects/data as our own, adding to our calculated footprint
template<typename CLAIM_T>
void claimMem(const CLAIM_T& value) const
{
#if LL_TRACE_ENABLED
S32 size = MeasureMem<CLAIM_T>::measureFootprint(value);
claim_alloc(sMemStat, size);
mMemFootprint += size;
#endif
}
// remove memory we had claimed from our calculated footprint
template<typename CLAIM_T>
void disclaimMem(const CLAIM_T& value) const
{
#if LL_TRACE_ENABLED
S32 size = MeasureMem<CLAIM_T>::measureFootprint(value);
disclaim_alloc(sMemStat, size);
mMemFootprint -= size;
#endif
}
private:
#if LL_TRACE_ENABLED
// use signed values so that we can temporarily go negative
// and reconcile in destructor
// NB: this assumes that no single class is responsible for > 2GB of allocations
mutable S32 mMemFootprint;
static MemStatHandle sMemStat;
#endif
};
#if LL_TRACE_ENABLED
template<typename DERIVED, size_t ALIGNMENT>
MemStatHandle MemTrackableNonVirtual<DERIVED, ALIGNMENT>::sMemStat(typeid(MemTrackableNonVirtual<DERIVED, ALIGNMENT>).name());
#endif
template<typename DERIVED, size_t ALIGNMENT = LL_DEFAULT_HEAP_ALIGN>
class MemTrackable : public MemTrackableNonVirtual<DERIVED, ALIGNMENT>
{
public:
MemTrackable(const char* name)
: MemTrackableNonVirtual<DERIVED, ALIGNMENT>(name)
{}
virtual ~MemTrackable()
{}
};
}
#endif // LL_LLTRACE_H

View File

@ -41,6 +41,7 @@ extern MemStatHandle gTraceMemStat;
AccumulatorBufferGroup::AccumulatorBufferGroup()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
claim_alloc(gTraceMemStat, mCounts.capacity() * sizeof(CountAccumulator));
claim_alloc(gTraceMemStat, mSamples.capacity() * sizeof(SampleAccumulator));
claim_alloc(gTraceMemStat, mEvents.capacity() * sizeof(EventAccumulator));
@ -55,6 +56,7 @@ AccumulatorBufferGroup::AccumulatorBufferGroup(const AccumulatorBufferGroup& oth
mStackTimers(other.mStackTimers),
mMemStats(other.mMemStats)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
claim_alloc(gTraceMemStat, mCounts.capacity() * sizeof(CountAccumulator));
claim_alloc(gTraceMemStat, mSamples.capacity() * sizeof(SampleAccumulator));
claim_alloc(gTraceMemStat, mEvents.capacity() * sizeof(EventAccumulator));
@ -64,6 +66,7 @@ AccumulatorBufferGroup::AccumulatorBufferGroup(const AccumulatorBufferGroup& oth
AccumulatorBufferGroup::~AccumulatorBufferGroup()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
disclaim_alloc(gTraceMemStat, mCounts.capacity() * sizeof(CountAccumulator));
disclaim_alloc(gTraceMemStat, mSamples.capacity() * sizeof(SampleAccumulator));
disclaim_alloc(gTraceMemStat, mEvents.capacity() * sizeof(EventAccumulator));
@ -73,6 +76,7 @@ AccumulatorBufferGroup::~AccumulatorBufferGroup()
void AccumulatorBufferGroup::handOffTo(AccumulatorBufferGroup& other)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
other.mCounts.reset(&mCounts);
other.mSamples.reset(&mSamples);
other.mEvents.reset(&mEvents);
@ -82,6 +86,7 @@ void AccumulatorBufferGroup::handOffTo(AccumulatorBufferGroup& other)
void AccumulatorBufferGroup::makeCurrent()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mCounts.makeCurrent();
mSamples.makeCurrent();
mEvents.makeCurrent();
@ -104,6 +109,7 @@ void AccumulatorBufferGroup::makeCurrent()
//static
void AccumulatorBufferGroup::clearCurrent()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
AccumulatorBuffer<CountAccumulator>::clearCurrent();
AccumulatorBuffer<SampleAccumulator>::clearCurrent();
AccumulatorBuffer<EventAccumulator>::clearCurrent();
@ -118,6 +124,7 @@ bool AccumulatorBufferGroup::isCurrent() const
void AccumulatorBufferGroup::append( const AccumulatorBufferGroup& other )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mCounts.addSamples(other.mCounts, SEQUENTIAL);
mSamples.addSamples(other.mSamples, SEQUENTIAL);
mEvents.addSamples(other.mEvents, SEQUENTIAL);
@ -127,6 +134,7 @@ void AccumulatorBufferGroup::append( const AccumulatorBufferGroup& other )
void AccumulatorBufferGroup::merge( const AccumulatorBufferGroup& other)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mCounts.addSamples(other.mCounts, NON_SEQUENTIAL);
mSamples.addSamples(other.mSamples, NON_SEQUENTIAL);
mEvents.addSamples(other.mEvents, NON_SEQUENTIAL);
@ -137,6 +145,7 @@ void AccumulatorBufferGroup::merge( const AccumulatorBufferGroup& other)
void AccumulatorBufferGroup::reset(AccumulatorBufferGroup* other)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mCounts.reset(other ? &other->mCounts : NULL);
mSamples.reset(other ? &other->mSamples : NULL);
mEvents.reset(other ? &other->mEvents : NULL);
@ -146,6 +155,7 @@ void AccumulatorBufferGroup::reset(AccumulatorBufferGroup* other)
void AccumulatorBufferGroup::sync()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
if (isCurrent())
{
F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds();
@ -190,7 +200,7 @@ F64 SampleAccumulator::mergeSumsOfSquares(const SampleAccumulator& a, const Samp
void SampleAccumulator::addSamples( const SampleAccumulator& other, EBufferAppendType append_type )
{
if (append_type == NON_SEQUENTIAL)
if (append_type == NON_SEQUENTIAL)
{
return;
}
@ -289,7 +299,7 @@ void EventAccumulator::addSamples( const EventAccumulator& other, EBufferAppendT
void EventAccumulator::reset( const EventAccumulator* other )
{
mNumSamples = 0;
mNumSamples = 0;
mSum = 0;
mMin = F32(NaN);
mMax = F32(NaN);

View File

@ -66,6 +66,7 @@ namespace LLTrace
: mStorageSize(0),
mStorage(NULL)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
const AccumulatorBuffer& other = *getDefaultBuffer();
resize(sNextStorageSlot);
for (S32 i = 0; i < sNextStorageSlot; i++)
@ -76,6 +77,7 @@ namespace LLTrace
~AccumulatorBuffer()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
if (isCurrent())
{
LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL);
@ -98,6 +100,7 @@ namespace LLTrace
: mStorageSize(0),
mStorage(NULL)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
resize(sNextStorageSlot);
for (S32 i = 0; i < sNextStorageSlot; i++)
{
@ -107,6 +110,7 @@ namespace LLTrace
void addSamples(const AccumulatorBuffer<ACCUMULATOR>& other, EBufferAppendType append_type)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize >= sNextStorageSlot);
for (size_t i = 0; i < sNextStorageSlot; i++)
{
@ -116,6 +120,7 @@ namespace LLTrace
void copyFrom(const AccumulatorBuffer<ACCUMULATOR>& other)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize >= sNextStorageSlot);
for (size_t i = 0; i < sNextStorageSlot; i++)
{
@ -125,6 +130,7 @@ namespace LLTrace
void reset(const AccumulatorBuffer<ACCUMULATOR>* other = NULL)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
llassert(mStorageSize >= sNextStorageSlot);
for (size_t i = 0; i < sNextStorageSlot; i++)
{
@ -134,6 +140,7 @@ namespace LLTrace
void sync(F64SecondsImplicit time_stamp)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
llassert(mStorageSize >= sNextStorageSlot);
for (size_t i = 0; i < sNextStorageSlot; i++)
{
@ -153,12 +160,13 @@ namespace LLTrace
static void clearCurrent()
{
LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL);
LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL);
}
// NOTE: this is not thread-safe. We assume that slots are reserved in the main thread before any child threads are spawned
size_t reserveSlot()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
size_t next_slot = sNextStorageSlot++;
if (next_slot >= mStorageSize)
{
@ -172,6 +180,7 @@ namespace LLTrace
void resize(size_t new_size)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
if (new_size <= mStorageSize) return;
ACCUMULATOR* old_storage = mStorage;
@ -212,6 +221,7 @@ namespace LLTrace
static self_t* getDefaultBuffer()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
static bool sInitialized = false;
if (!sInitialized)
{
@ -326,6 +336,7 @@ namespace LLTrace
void sample(F64 value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds();
// store effect of last value
@ -444,9 +455,9 @@ namespace LLTrace
S32 mNumSamples;
};
class TimeBlockAccumulator
class alignas(32) TimeBlockAccumulator
{
public:
public:
typedef F64Seconds value_t;
static F64Seconds getDefaultValue() { return F64Seconds(0); }
@ -539,6 +550,7 @@ namespace LLTrace
void addSamples(const MemAccumulator& other, EBufferAppendType append_type)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mAllocations.addSamples(other.mAllocations, append_type);
mDeallocations.addSamples(other.mDeallocations, append_type);
@ -557,6 +569,7 @@ namespace LLTrace
void reset(const MemAccumulator* other)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mSize.reset(other ? &other->mSize : NULL);
mAllocations.reset(other ? &other->mAllocations : NULL);
mDeallocations.reset(other ? &other->mDeallocations : NULL);

View File

@ -50,6 +50,7 @@ Recording::Recording(EPlayState state)
: mElapsedSeconds(0),
mActiveBuffers(NULL)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
claim_alloc(gTraceMemStat, this);
mBuffers = new AccumulatorBufferGroup();
claim_alloc(gTraceMemStat, mBuffers);
@ -59,12 +60,14 @@ Recording::Recording(EPlayState state)
Recording::Recording( const Recording& other )
: mActiveBuffers(NULL)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
claim_alloc(gTraceMemStat, this);
*this = other;
}
Recording& Recording::operator = (const Recording& other)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
// this will allow us to seamlessly start without affecting any data we've acquired from other
setPlayState(PAUSED);
@ -85,6 +88,7 @@ Recording& Recording::operator = (const Recording& other)
Recording::~Recording()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
disclaim_alloc(gTraceMemStat, this);
disclaim_alloc(gTraceMemStat, mBuffers);
@ -103,6 +107,7 @@ void Recording::update()
#if LL_TRACE_ENABLED
if (isStarted())
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
// must have
@ -123,6 +128,7 @@ void Recording::update()
void Recording::handleReset()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
#if LL_TRACE_ENABLED
mBuffers.write()->reset();
@ -133,6 +139,7 @@ void Recording::handleReset()
void Recording::handleStart()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
#if LL_TRACE_ENABLED
mSamplingTimer.reset();
mBuffers.setStayUnique(true);
@ -144,6 +151,7 @@ void Recording::handleStart()
void Recording::handleStop()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
#if LL_TRACE_ENABLED
mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
// must have thread recorder running on this thread
@ -273,7 +281,7 @@ F64Kilobytes Recording::getMean(const StatType<MemAccumulator>& stat)
F64Kilobytes Recording::getMax(const StatType<MemAccumulator>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return F64Bytes(llmax(accumulator.mSize.getMax(), active_accumulator && active_accumulator->mSize.hasValue() ? active_accumulator->mSize.getMax() : F32_MIN));
@ -281,7 +289,7 @@ F64Kilobytes Recording::getMax(const StatType<MemAccumulator>& stat)
F64Kilobytes Recording::getStandardDeviation(const StatType<MemAccumulator>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
if (active_accumulator && active_accumulator->hasValue())
@ -297,7 +305,7 @@ F64Kilobytes Recording::getStandardDeviation(const StatType<MemAccumulator>& sta
F64Kilobytes Recording::getLastValue(const StatType<MemAccumulator>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return F64Bytes(active_accumulator ? active_accumulator->mSize.getLastValue() : accumulator.mSize.getLastValue());
@ -305,7 +313,7 @@ F64Kilobytes Recording::getLastValue(const StatType<MemAccumulator>& stat)
bool Recording::hasValue(const StatType<MemAccumulator::AllocationFacet>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return accumulator.mAllocations.hasValue() || (active_accumulator ? active_accumulator->mAllocations.hasValue() : false);
@ -313,7 +321,7 @@ bool Recording::hasValue(const StatType<MemAccumulator::AllocationFacet>& stat)
F64Kilobytes Recording::getSum(const StatType<MemAccumulator::AllocationFacet>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return F64Bytes(accumulator.mAllocations.getSum() + (active_accumulator ? active_accumulator->mAllocations.getSum() : 0));
@ -321,7 +329,7 @@ F64Kilobytes Recording::getSum(const StatType<MemAccumulator::AllocationFacet>&
F64Kilobytes Recording::getPerSec(const StatType<MemAccumulator::AllocationFacet>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return F64Bytes((accumulator.mAllocations.getSum() + (active_accumulator ? active_accumulator->mAllocations.getSum() : 0)) / mElapsedSeconds.value());
@ -329,7 +337,7 @@ F64Kilobytes Recording::getPerSec(const StatType<MemAccumulator::AllocationFacet
S32 Recording::getSampleCount(const StatType<MemAccumulator::AllocationFacet>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return accumulator.mAllocations.getSampleCount() + (active_accumulator ? active_accumulator->mAllocations.getSampleCount() : 0);
@ -337,7 +345,7 @@ S32 Recording::getSampleCount(const StatType<MemAccumulator::AllocationFacet>& s
bool Recording::hasValue(const StatType<MemAccumulator::DeallocationFacet>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return accumulator.mDeallocations.hasValue() || (active_accumulator ? active_accumulator->mDeallocations.hasValue() : false);
@ -346,7 +354,7 @@ bool Recording::hasValue(const StatType<MemAccumulator::DeallocationFacet>& stat
F64Kilobytes Recording::getSum(const StatType<MemAccumulator::DeallocationFacet>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return F64Bytes(accumulator.mDeallocations.getSum() + (active_accumulator ? active_accumulator->mDeallocations.getSum() : 0));
@ -354,7 +362,7 @@ F64Kilobytes Recording::getSum(const StatType<MemAccumulator::DeallocationFacet>
F64Kilobytes Recording::getPerSec(const StatType<MemAccumulator::DeallocationFacet>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return F64Bytes((accumulator.mDeallocations.getSum() + (active_accumulator ? active_accumulator->mDeallocations.getSum() : 0)) / mElapsedSeconds.value());
@ -362,7 +370,7 @@ F64Kilobytes Recording::getPerSec(const StatType<MemAccumulator::DeallocationFac
S32 Recording::getSampleCount(const StatType<MemAccumulator::DeallocationFacet>& stat)
{
update();
update();
const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
return accumulator.mDeallocations.getSampleCount() + (active_accumulator ? active_accumulator->mDeallocations.getSampleCount() : 0);
@ -370,7 +378,7 @@ S32 Recording::getSampleCount(const StatType<MemAccumulator::DeallocationFacet>&
bool Recording::hasValue(const StatType<CountAccumulator>& stat)
{
update();
update();
const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()];
const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL;
return accumulator.hasValue() || (active_accumulator ? active_accumulator->hasValue() : false);
@ -378,7 +386,7 @@ bool Recording::hasValue(const StatType<CountAccumulator>& stat)
F64 Recording::getSum(const StatType<CountAccumulator>& stat)
{
update();
update();
const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()];
const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL;
return accumulator.getSum() + (active_accumulator ? active_accumulator->getSum() : 0);
@ -386,7 +394,7 @@ F64 Recording::getSum(const StatType<CountAccumulator>& stat)
F64 Recording::getPerSec( const StatType<CountAccumulator>& stat )
{
update();
update();
const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()];
const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL;
F64 sum = accumulator.getSum() + (active_accumulator ? active_accumulator->getSum() : 0);
@ -395,7 +403,7 @@ F64 Recording::getPerSec( const StatType<CountAccumulator>& stat )
S32 Recording::getSampleCount( const StatType<CountAccumulator>& stat )
{
update();
update();
const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()];
const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL;
return accumulator.getSampleCount() + (active_accumulator ? active_accumulator->getSampleCount() : 0);
@ -403,7 +411,7 @@ S32 Recording::getSampleCount( const StatType<CountAccumulator>& stat )
bool Recording::hasValue(const StatType<SampleAccumulator>& stat)
{
update();
update();
const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue());
@ -411,7 +419,7 @@ bool Recording::hasValue(const StatType<SampleAccumulator>& stat)
F64 Recording::getMin( const StatType<SampleAccumulator>& stat )
{
update();
update();
const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
return llmin(accumulator.getMin(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMin() : F32_MAX);
@ -419,7 +427,7 @@ F64 Recording::getMin( const StatType<SampleAccumulator>& stat )
F64 Recording::getMax( const StatType<SampleAccumulator>& stat )
{
update();
update();
const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
return llmax(accumulator.getMax(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMax() : F32_MIN);
@ -427,7 +435,7 @@ F64 Recording::getMax( const StatType<SampleAccumulator>& stat )
F64 Recording::getMean( const StatType<SampleAccumulator>& stat )
{
update();
update();
const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
if (active_accumulator && active_accumulator->hasValue())
@ -448,7 +456,7 @@ F64 Recording::getMean( const StatType<SampleAccumulator>& stat )
F64 Recording::getStandardDeviation( const StatType<SampleAccumulator>& stat )
{
update();
update();
const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
@ -465,7 +473,7 @@ F64 Recording::getStandardDeviation( const StatType<SampleAccumulator>& stat )
F64 Recording::getLastValue( const StatType<SampleAccumulator>& stat )
{
update();
update();
const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
return (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getLastValue() : accumulator.getLastValue());
@ -473,7 +481,7 @@ F64 Recording::getLastValue( const StatType<SampleAccumulator>& stat )
S32 Recording::getSampleCount( const StatType<SampleAccumulator>& stat )
{
update();
update();
const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
return accumulator.getSampleCount() + (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getSampleCount() : 0);
@ -481,7 +489,7 @@ S32 Recording::getSampleCount( const StatType<SampleAccumulator>& stat )
bool Recording::hasValue(const StatType<EventAccumulator>& stat)
{
update();
update();
const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue());
@ -489,7 +497,7 @@ bool Recording::hasValue(const StatType<EventAccumulator>& stat)
F64 Recording::getSum( const StatType<EventAccumulator>& stat)
{
update();
update();
const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
return (F64)(accumulator.getSum() + (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getSum() : 0));
@ -497,7 +505,7 @@ F64 Recording::getSum( const StatType<EventAccumulator>& stat)
F64 Recording::getMin( const StatType<EventAccumulator>& stat )
{
update();
update();
const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
return llmin(accumulator.getMin(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMin() : F32_MAX);
@ -505,7 +513,7 @@ F64 Recording::getMin( const StatType<EventAccumulator>& stat )
F64 Recording::getMax( const StatType<EventAccumulator>& stat )
{
update();
update();
const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
return llmax(accumulator.getMax(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMax() : F32_MIN);
@ -513,7 +521,7 @@ F64 Recording::getMax( const StatType<EventAccumulator>& stat )
F64 Recording::getMean( const StatType<EventAccumulator>& stat )
{
update();
update();
const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
if (active_accumulator && active_accumulator->hasValue())
@ -534,7 +542,7 @@ F64 Recording::getMean( const StatType<EventAccumulator>& stat )
F64 Recording::getStandardDeviation( const StatType<EventAccumulator>& stat )
{
update();
update();
const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
@ -551,7 +559,7 @@ F64 Recording::getStandardDeviation( const StatType<EventAccumulator>& stat )
F64 Recording::getLastValue( const StatType<EventAccumulator>& stat )
{
update();
update();
const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
return active_accumulator ? active_accumulator->getLastValue() : accumulator.getLastValue();
@ -559,7 +567,7 @@ F64 Recording::getLastValue( const StatType<EventAccumulator>& stat )
S32 Recording::getSampleCount( const StatType<EventAccumulator>& stat )
{
update();
update();
const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
return accumulator.getSampleCount() + (active_accumulator ? active_accumulator->getSampleCount() : 0);
@ -575,17 +583,20 @@ PeriodicRecording::PeriodicRecording( S32 num_periods, EPlayState state)
mNumRecordedPeriods(0),
mRecordingPeriods(num_periods ? num_periods : 1)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
setPlayState(state);
claim_alloc(gTraceMemStat, this);
}
PeriodicRecording::~PeriodicRecording()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
disclaim_alloc(gTraceMemStat, this);
}
void PeriodicRecording::nextPeriod()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
if (mAutoResize)
{
mRecordingPeriods.push_back(Recording());
@ -600,6 +611,7 @@ void PeriodicRecording::nextPeriod()
void PeriodicRecording::appendRecording(Recording& recording)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
getCurRecording().appendRecording(recording);
nextPeriod();
}
@ -607,6 +619,7 @@ void PeriodicRecording::appendRecording(Recording& recording)
void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
if (other.mRecordingPeriods.empty()) return;
getCurRecording().update();
@ -680,6 +693,7 @@ void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other )
F64Seconds PeriodicRecording::getDuration() const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
F64Seconds duration;
S32 num_periods = mRecordingPeriods.size();
for (S32 i = 1; i <= num_periods; i++)
@ -693,6 +707,7 @@ F64Seconds PeriodicRecording::getDuration() const
LLTrace::Recording PeriodicRecording::snapshotCurRecording() const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
Recording recording_copy(getCurRecording());
recording_copy.stop();
return recording_copy;
@ -735,16 +750,19 @@ const Recording& PeriodicRecording::getPrevRecording( S32 offset ) const
void PeriodicRecording::handleStart()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
getCurRecording().start();
}
void PeriodicRecording::handleStop()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
getCurRecording().pause();
}
void PeriodicRecording::handleReset()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
getCurRecording().stop();
if (mAutoResize)
@ -768,11 +786,13 @@ void PeriodicRecording::handleReset()
void PeriodicRecording::handleSplitTo(PeriodicRecording& other)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
getCurRecording().splitTo(other.getCurRecording());
}
F64 PeriodicRecording::getPeriodMin( const StatType<EventAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
bool has_value = false;
@ -794,6 +814,7 @@ F64 PeriodicRecording::getPeriodMin( const StatType<EventAccumulator>& stat, S32
F64 PeriodicRecording::getPeriodMax( const StatType<EventAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
bool has_value = false;
@ -816,6 +837,7 @@ F64 PeriodicRecording::getPeriodMax( const StatType<EventAccumulator>& stat, S32
// calculates means using aggregates per period
F64 PeriodicRecording::getPeriodMean( const StatType<EventAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
F64 mean = 0;
@ -836,9 +858,9 @@ F64 PeriodicRecording::getPeriodMean( const StatType<EventAccumulator>& stat, S3
: NaN;
}
F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<EventAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
F64 period_mean = getPeriodMean(stat, num_periods);
@ -863,6 +885,7 @@ F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<EventAccumulat
F64 PeriodicRecording::getPeriodMin( const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
bool has_value = false;
@ -884,6 +907,7 @@ F64 PeriodicRecording::getPeriodMin( const StatType<SampleAccumulator>& stat, S3
F64 PeriodicRecording::getPeriodMax(const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
bool has_value = false;
@ -906,6 +930,7 @@ F64 PeriodicRecording::getPeriodMax(const StatType<SampleAccumulator>& stat, S32
F64 PeriodicRecording::getPeriodMean( const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
S32 valid_period_count = 0;
@ -926,8 +951,35 @@ F64 PeriodicRecording::getPeriodMean( const StatType<SampleAccumulator>& stat, S
: NaN;
}
F64 PeriodicRecording::getPeriodMedian( const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
std::vector<F64> buf;
for (S32 i = 1; i <= num_periods; i++)
{
Recording& recording = getPrevRecording(i);
if (recording.getDuration() > (F32Seconds)0.f)
{
if (recording.hasValue(stat))
{
buf.push_back(recording.getMean(stat));
}
}
}
if (buf.size()==0)
{
return 0.0f;
}
std::sort(buf.begin(), buf.end());
return F64((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]);
}
F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
F64 period_mean = getPeriodMean(stat, num_periods);
@ -953,6 +1005,7 @@ F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<SampleAccumula
F64Kilobytes PeriodicRecording::getPeriodMin( const StatType<MemAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
F64Kilobytes min_val(std::numeric_limits<F64>::max());
@ -972,6 +1025,7 @@ F64Kilobytes PeriodicRecording::getPeriodMin(const MemStatHandle& stat, S32 num_
F64Kilobytes PeriodicRecording::getPeriodMax(const StatType<MemAccumulator>& stat, S32 num_periods /*= S32_MAX*/)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
F64Kilobytes max_val(0.0);
@ -991,6 +1045,7 @@ F64Kilobytes PeriodicRecording::getPeriodMax(const MemStatHandle& stat, S32 num_
F64Kilobytes PeriodicRecording::getPeriodMean( const StatType<MemAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
F64Kilobytes mean(0);
@ -1011,6 +1066,7 @@ F64Kilobytes PeriodicRecording::getPeriodMean(const MemStatHandle& stat, S32 num
F64Kilobytes PeriodicRecording::getPeriodStandardDeviation( const StatType<MemAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
F64Kilobytes period_mean = getPeriodMean(stat, num_periods);
@ -1044,6 +1100,7 @@ F64Kilobytes PeriodicRecording::getPeriodStandardDeviation(const MemStatHandle&
void ExtendableRecording::extend()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
// push the data back to accepted recording
mAcceptedRecording.appendRecording(mPotentialRecording);
// flush data, so we can start from scratch
@ -1052,22 +1109,26 @@ void ExtendableRecording::extend()
void ExtendableRecording::handleStart()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mPotentialRecording.start();
}
void ExtendableRecording::handleStop()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mPotentialRecording.pause();
}
void ExtendableRecording::handleReset()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mAcceptedRecording.reset();
mPotentialRecording.reset();
}
void ExtendableRecording::handleSplitTo(ExtendableRecording& other)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mPotentialRecording.splitTo(other.mPotentialRecording);
}
@ -1084,6 +1145,7 @@ ExtendablePeriodicRecording::ExtendablePeriodicRecording()
void ExtendablePeriodicRecording::extend()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
// push the data back to accepted recording
mAcceptedRecording.appendPeriodicRecording(mPotentialRecording);
// flush data, so we can start from scratch
@ -1093,22 +1155,26 @@ void ExtendablePeriodicRecording::extend()
void ExtendablePeriodicRecording::handleStart()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mPotentialRecording.start();
}
void ExtendablePeriodicRecording::handleStop()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mPotentialRecording.pause();
}
void ExtendablePeriodicRecording::handleReset()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mAcceptedRecording.reset();
mPotentialRecording.reset();
}
void ExtendablePeriodicRecording::handleSplitTo(ExtendablePeriodicRecording& other)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
mPotentialRecording.splitTo(other.mPotentialRecording);
}
@ -1123,6 +1189,7 @@ PeriodicRecording& get_frame_recording()
void LLStopWatchControlsMixinCommon::start()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
switch (mPlayState)
{
case STOPPED:
@ -1144,6 +1211,7 @@ void LLStopWatchControlsMixinCommon::start()
void LLStopWatchControlsMixinCommon::stop()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
switch (mPlayState)
{
case STOPPED:
@ -1163,6 +1231,7 @@ void LLStopWatchControlsMixinCommon::stop()
void LLStopWatchControlsMixinCommon::pause()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
switch (mPlayState)
{
case STOPPED:
@ -1182,6 +1251,7 @@ void LLStopWatchControlsMixinCommon::pause()
void LLStopWatchControlsMixinCommon::unpause()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
switch (mPlayState)
{
case STOPPED:
@ -1201,6 +1271,7 @@ void LLStopWatchControlsMixinCommon::unpause()
void LLStopWatchControlsMixinCommon::resume()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
switch (mPlayState)
{
case STOPPED:
@ -1221,6 +1292,7 @@ void LLStopWatchControlsMixinCommon::resume()
void LLStopWatchControlsMixinCommon::restart()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
switch (mPlayState)
{
case STOPPED:
@ -1244,11 +1316,13 @@ void LLStopWatchControlsMixinCommon::restart()
void LLStopWatchControlsMixinCommon::reset()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
handleReset();
}
void LLStopWatchControlsMixinCommon::setPlayState( EPlayState state )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
switch(state)
{
case STOPPED:

View File

@ -355,6 +355,7 @@ namespace LLTrace
template <typename T>
S32 getSampleCount(const StatType<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
S32 num_samples = 0;
@ -374,6 +375,7 @@ namespace LLTrace
template <typename T>
typename T::value_t getPeriodMin(const StatType<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
bool has_value = false;
@ -396,6 +398,7 @@ namespace LLTrace
template<typename T>
T getPeriodMin(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return T(getPeriodMin(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
}
@ -403,6 +406,7 @@ namespace LLTrace
template<typename T>
T getPeriodMin(const SampleStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return T(getPeriodMin(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods));
}
@ -410,6 +414,7 @@ namespace LLTrace
template<typename T>
T getPeriodMin(const EventStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return T(getPeriodMin(static_cast<const StatType<EventAccumulator>&>(stat), num_periods));
}
@ -419,6 +424,7 @@ namespace LLTrace
template <typename T>
typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMinPerSec(const StatType<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
typename RelatedTypes<typename T::value_t>::fractional_t min_val(std::numeric_limits<F64>::max());
@ -433,6 +439,7 @@ namespace LLTrace
template<typename T>
typename RelatedTypes<T>::fractional_t getPeriodMinPerSec(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return typename RelatedTypes<T>::fractional_t(getPeriodMinPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
}
@ -444,6 +451,7 @@ namespace LLTrace
template <typename T>
typename T::value_t getPeriodMax(const StatType<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
bool has_value = false;
@ -466,6 +474,7 @@ namespace LLTrace
template<typename T>
T getPeriodMax(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return T(getPeriodMax(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
}
@ -473,6 +482,7 @@ namespace LLTrace
template<typename T>
T getPeriodMax(const SampleStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return T(getPeriodMax(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods));
}
@ -480,6 +490,7 @@ namespace LLTrace
template<typename T>
T getPeriodMax(const EventStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return T(getPeriodMax(static_cast<const StatType<EventAccumulator>&>(stat), num_periods));
}
@ -489,6 +500,7 @@ namespace LLTrace
template <typename T>
typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMaxPerSec(const StatType<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
F64 max_val = std::numeric_limits<F64>::min();
@ -503,6 +515,7 @@ namespace LLTrace
template<typename T>
typename RelatedTypes<T>::fractional_t getPeriodMaxPerSec(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return typename RelatedTypes<T>::fractional_t(getPeriodMaxPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
}
@ -514,6 +527,7 @@ namespace LLTrace
template <typename T>
typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMean(const StatType<T >& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
typename RelatedTypes<typename T::value_t>::fractional_t mean(0);
@ -534,12 +548,14 @@ namespace LLTrace
template<typename T>
typename RelatedTypes<T>::fractional_t getPeriodMean(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
}
F64 getPeriodMean(const StatType<SampleAccumulator>& stat, S32 num_periods = S32_MAX);
template<typename T>
typename RelatedTypes<T>::fractional_t getPeriodMean(const SampleStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods));
}
@ -547,6 +563,7 @@ namespace LLTrace
template<typename T>
typename RelatedTypes<T>::fractional_t getPeriodMean(const EventStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<EventAccumulator>&>(stat), num_periods));
}
@ -556,6 +573,7 @@ namespace LLTrace
template <typename T>
typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMeanPerSec(const StatType<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
typename RelatedTypes<typename T::value_t>::fractional_t mean = 0;
@ -577,12 +595,16 @@ namespace LLTrace
template<typename T>
typename RelatedTypes<T>::fractional_t getPeriodMeanPerSec(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return typename RelatedTypes<T>::fractional_t(getPeriodMeanPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
}
F64 getPeriodMedian( const StatType<SampleAccumulator>& stat, S32 num_periods = S32_MAX);
template <typename T>
typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMedianPerSec(const StatType<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
num_periods = llmin(num_periods, getNumRecordedPeriods());
std::vector <typename RelatedTypes<typename T::value_t>::fractional_t> buf;
@ -602,6 +624,7 @@ namespace LLTrace
template<typename T>
typename RelatedTypes<T>::fractional_t getPeriodMedianPerSec(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return typename RelatedTypes<T>::fractional_t(getPeriodMedianPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
}
@ -614,6 +637,7 @@ namespace LLTrace
template<typename T>
typename RelatedTypes<T>::fractional_t getPeriodStandardDeviation(const SampleStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return typename RelatedTypes<T>::fractional_t(getPeriodStandardDeviation(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods));
}
@ -621,6 +645,7 @@ namespace LLTrace
template<typename T>
typename RelatedTypes<T>::fractional_t getPeriodStandardDeviation(const EventStatHandle<T>& stat, S32 num_periods = S32_MAX)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
return typename RelatedTypes<T>::fractional_t(getPeriodStandardDeviation(static_cast<const StatType<EventAccumulator>&>(stat), num_periods));
}

View File

@ -274,12 +274,10 @@ void ThreadRecorder::pushToParent()
}
static LLTrace::BlockTimerStatHandle FTM_PULL_TRACE_DATA_FROM_CHILDREN("Pull child thread trace data");
void ThreadRecorder::pullFromChildren()
{
#if LL_TRACE_ENABLED
LL_RECORD_BLOCK_TIME(FTM_PULL_TRACE_DATA_FROM_CHILDREN);
LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS;
if (mActiveRecordings.empty()) return;
{ LLMutexLock lock(&mChildListMutex);

View File

@ -45,7 +45,7 @@
#include "lltimer.h"
#include "llthread.h"
#include "llmutex.h"
#include "fstelemetry.h"
#include "llprofiler.h"
const LLUUID LLUUID::null;
const LLTransactionID LLTransactionID::tnull;
@ -156,7 +156,7 @@ U32 janky_fast_random_seeded_bytes(U32 seed, U32 val)
// Common to all UUID implementations
void LLUUID::toString(std::string& out) const
{
FSZone;
LL_PROFILE_ZONE_SCOPED;
out = llformat(
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
(U8)(mData[0]),
@ -219,7 +219,7 @@ BOOL LLUUID::set(const char* in_string, BOOL emit)
BOOL LLUUID::set(const std::string& in_string, BOOL emit)
{
FSZone;
LL_PROFILE_ZONE_SCOPED;
BOOL broken_format = FALSE;
// empty strings should make NULL uuid
@ -1030,7 +1030,7 @@ LLUUID::LLUUID()
// Copy constructor
LLUUID::LLUUID(const LLUUID& rhs)
{
FSZone;
LL_PROFILE_ZONE_SCOPED;
// <FS> Fix for misaligned unsigned ints in LLUUID; by Sovereign Engineer / Shyotl Kuhr
//U32 *tmp = (U32 *)mData;
//U32 *rhstmp = (U32 *)rhs.mData;
@ -1049,7 +1049,7 @@ LLUUID::LLUUID()
// Assignment
LLUUID& LLUUID::operator=(const LLUUID& rhs)
{
FSZone;
LL_PROFILE_ZONE_SCOPED;
// <FS> Fix for misaligned unsigned ints in LLUUID; by Sovereign Engineer / Shyotl Kuhr
//// No need to check the case where this==&rhs. The branch is slower than the write.
//U32 *tmp = (U32 *)mData;

View File

@ -33,7 +33,7 @@
#include "stdtypes.h"
#include "llpreprocessor.h"
#include <boost/functional/hash.hpp>
#include "fstelemetry.h"
#include "llprofiler.h"
class LLMutex;
const S32 UUID_BYTES = 16;
@ -57,9 +57,9 @@ public:
explicit LLUUID(const char *in_string); // Convert from string.
explicit LLUUID(const std::string& in_string); // Convert from string.
LLUUID(const LLUUID &in);
LLUUID(LLUUID&& rhs) noexcept { FSZone; std::memmove(mData, rhs.mData, sizeof(mData));};
LLUUID(LLUUID&& rhs) noexcept { LL_PROFILE_ZONE_SCOPED; std::memmove(mData, rhs.mData, sizeof(mData));};
LLUUID &operator=(const LLUUID &rhs);
LLUUID &operator=(LLUUID &&rhs) noexcept {FSZone; std::memmove(mData, rhs.mData, sizeof(mData));return *this;};
LLUUID &operator=(LLUUID &&rhs) noexcept { LL_PROFILE_ZONE_SCOPED; std::memmove(mData, rhs.mData, sizeof(mData));return *this;};
~LLUUID();
@ -120,7 +120,7 @@ public:
# define hexnybl(N) (N)>9?((N)-10)+'a':(N)+'0'
inline char * toShortString(char *out) const
{
FSZone;
LL_PROFILE_ZONE_SCOPED;
out[0] = hexnybl(mData[14]>>4);
out[1] = hexnybl(mData[14]&15);
out[2] = hexnybl(mData[15]>>4);
@ -130,7 +130,7 @@ public:
// full uuid - Much lighterweight than default, no allocation, or nul-term added - provide your own, ensure min 36 bytes.
inline char * toStringFast(char *out) const
{
FSZone;
LL_PROFILE_ZONE_SCOPED;
out[0] = hexnybl(mData[0]>>4);
out[1] = hexnybl(mData[0]&15);
out[2] = hexnybl(mData[1]>>4);
@ -259,6 +259,17 @@ struct FSUUIDHash
};
// </FS:Ansariel> UUID hash calculation
// Adapt boost hash to std hash
namespace std
{
template<> struct hash<LLUUID>
{
std::size_t operator()(LLUUID const& s) const noexcept
{
return boost::hash<LLUUID>()(s);
}
};
}
#endif

View File

@ -42,10 +42,17 @@ typedef unsigned int U32;
// Windows wchar_t is 16-bit, whichever way /Zc:wchar_t is set. In effect,
// Windows wchar_t is always a typedef, either for unsigned short or __wchar_t.
// (__wchar_t, available either way, is Microsoft's native 2-byte wchar_t type.)
// The version of clang available with VS 2019 also defines wchar_t as __wchar_t
// which is also 16 bits.
// In any case, llwchar should be a UTF-32 type.
typedef U32 llwchar;
#else
typedef wchar_t llwchar;
// What we'd actually want is a simple module-scope 'if constexpr' to test
// std::is_same<wchar_t, llwchar>::value and use that to define, or not
// define, string conversion specializations. Since we don't have that, we'll
// have to rely on #if instead. Sorry, Dr. Stroustrup.
#define LLWCHAR_IS_WCHAR_T 1
#endif
#if LL_WINDOWS

View File

@ -31,58 +31,109 @@
#include <sstream>
#include <llstring.h>
#include <boost/call_traits.hpp>
/**
* gstringize(item) encapsulates an idiom we use constantly, using
* operator<<(std::ostringstream&, TYPE) followed by std::ostringstream::str()
* or their wstring equivalents
* to render a string expressing some item.
* stream_to(std::ostream&, items, ...) streams each item in the parameter list
* to the passed std::ostream using the insertion operator <<. This can be
* used, for instance, to make a simple print() function, e.g.:
*
* @code
* template <typename... Items>
* void print(Items&&... items)
* {
* stream_to(std::cout, std::forward<Items>(items)...);
* }
* @endcode
*/
template <typename CHARTYPE, typename T>
std::basic_string<CHARTYPE> gstringize(const T& item)
// recursion tail
template <typename CHARTYPE>
void stream_to(std::basic_ostream<CHARTYPE>& out) {}
// stream one or more items
template <typename CHARTYPE, typename T, typename... Items>
void stream_to(std::basic_ostream<CHARTYPE>& out, T&& item, Items&&... items)
{
out << std::forward<T>(item);
stream_to(out, std::forward<Items>(items)...);
}
// why we use function overloads, not function template specializations:
// http://www.gotw.ca/publications/mill17.htm
/**
* gstringize(item, ...) encapsulates an idiom we use constantly, using
* operator<<(std::ostringstream&, TYPE) followed by std::ostringstream::str()
* or their wstring equivalents to render a string expressing one or more items.
*/
// two or more args - the case of a single argument is handled separately
template <typename CHARTYPE, typename T0, typename T1, typename... Items>
auto gstringize(T0&& item0, T1&& item1, Items&&... items)
{
std::basic_ostringstream<CHARTYPE> out;
out << item;
stream_to(out, std::forward<T0>(item0), std::forward<T1>(item1),
std::forward<Items>(items)...);
return out.str();
}
/**
*partial specialization of stringize for handling wstring
*TODO: we should have similar specializations for wchar_t[] but not until it is needed.
*/
inline std::string stringize(const std::wstring& item)
// generic single argument: stream to out, as above
template <typename CHARTYPE, typename T>
struct gstringize_impl
{
return wstring_to_utf8str(item);
auto operator()(typename boost::call_traits<T>::param_type arg)
{
std::basic_ostringstream<CHARTYPE> out;
out << arg;
return out.str();
}
};
// partially specialize for a single STRING argument -
// note that ll_convert<T>(T) already handles the trivial case
template <typename OUTCHAR, typename INCHAR>
struct gstringize_impl<OUTCHAR, std::basic_string<INCHAR>>
{
auto operator()(const std::basic_string<INCHAR>& arg)
{
return ll_convert<std::basic_string<OUTCHAR>>(arg);
}
};
// partially specialize for a single CHARTYPE* argument -
// since it's not a basic_string and we do want to optimize this common case
template <typename OUTCHAR, typename INCHAR>
struct gstringize_impl<OUTCHAR, INCHAR*>
{
auto operator()(const INCHAR* arg)
{
return ll_convert<std::basic_string<OUTCHAR>>(arg);
}
};
// gstringize(single argument)
template <typename CHARTYPE, typename T>
auto gstringize(T&& item)
{
// use decay<T> so we don't require separate specializations
// for T, const T, T&, const T& ...
return gstringize_impl<CHARTYPE, std::decay_t<T>>()(std::forward<T>(item));
}
/**
* Specialization of gstringize for std::string return types
*/
template <typename T>
std::string stringize(const T& item)
template <typename... Items>
auto stringize(Items&&... items)
{
return gstringize<char>(item);
}
/**
* Specialization for generating wstring from string.
* Both a convenience function and saves a miniscule amount of overhead.
*/
inline std::wstring wstringize(const std::string& item)
{
// utf8str_to_wstring() returns LLWString, which isn't necessarily the
// same as std::wstring
LLWString s(utf8str_to_wstring(item));
return std::wstring(s.begin(), s.end());
return gstringize<char>(std::forward<Items>(items)...);
}
/**
* Specialization of gstringize for std::wstring return types
*/
template <typename T>
std::wstring wstringize(const T& item)
template <typename... Items>
auto wstringize(Items&&... items)
{
return gstringize<wchar_t>(item);
return gstringize<wchar_t>(std::forward<Items>(items)...);
}
/**
@ -146,11 +197,9 @@ void destringize_f(std::basic_string<CHARTYPE> const & str, Functor const & f)
* std::istringstream in(str);
* in >> item1 >> item2 >> item3 ... ;
* @endcode
* @NOTE - once we get generic lambdas, we shouldn't need DEWSTRINGIZE() any
* more since DESTRINGIZE() should do the right thing with a std::wstring. But
* until then, the lambda we pass must accept the right std::basic_istream.
*/
#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), [&](std::istream& in){in >> EXPRESSION;}))
#define DEWSTRINGIZE(STR, EXPRESSION) (destringize_f((STR), [&](std::wistream& in){in >> EXPRESSION;}))
#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), [&](auto& in){in >> EXPRESSION;}))
// legacy name, just use DESTRINGIZE() going forward
#define DEWSTRINGIZE(STR, EXPRESSION) DESTRINGIZE(STR, EXPRESSION)
#endif /* ! defined(LL_STRINGIZE_H) */

View File

@ -90,19 +90,19 @@ namespace tut
{
Keyed one("one");
ensure_equals(Keyed::instanceCount(), 1);
Keyed* found = Keyed::getInstance("one");
ensure("couldn't find stack Keyed", found);
ensure_equals("found wrong Keyed instance", found, &one);
auto found = Keyed::getInstance("one");
ensure("couldn't find stack Keyed", bool(found));
ensure_equals("found wrong Keyed instance", found.get(), &one);
{
boost::scoped_ptr<Keyed> two(new Keyed("two"));
ensure_equals(Keyed::instanceCount(), 2);
Keyed* found = Keyed::getInstance("two");
ensure("couldn't find heap Keyed", found);
ensure_equals("found wrong Keyed instance", found, two.get());
auto found = Keyed::getInstance("two");
ensure("couldn't find heap Keyed", bool(found));
ensure_equals("found wrong Keyed instance", found.get(), two.get());
}
ensure_equals(Keyed::instanceCount(), 1);
}
Keyed* found = Keyed::getInstance("one");
auto found = Keyed::getInstance("one");
ensure("Keyed key lives too long", ! found);
ensure_equals(Keyed::instanceCount(), 0);
}

View File

@ -356,14 +356,15 @@ namespace tut
// Create a script file in a temporary place.
NamedTempFile script("py",
"from __future__ import print_function" EOL
"import sys" EOL
"import time" EOL
EOL
"time.sleep(2)" EOL
"print('stdout after wait', file=sys.stdout)" EOL
"print('stdout after wait',file=sys.stdout)" EOL
"sys.stdout.flush()" EOL
"time.sleep(2)" EOL
"print('stderr after wait', file=sys.stderr)" EOL
"print('stderr after wait',file=sys.stderr)" EOL
"sys.stderr.flush()" EOL
);
@ -572,12 +573,12 @@ namespace tut
{
set_test_name("arguments");
PythonProcessLauncher py(get_test_name(),
"from __future__ import with_statement\n"
"from __future__ import with_statement, print_function\n"
"import sys\n"
// note nonstandard output-file arg!
"with open(sys.argv[3], 'w') as f:\n"
" for arg in sys.argv[1:]:\n"
" print(arg, file=f)\n");
" print(arg,file=f)\n");
// We expect that PythonProcessLauncher has already appended
// its own NamedTempFile to mParams.args (sys.argv[0]).
py.mParams.args.add("first arg"); // sys.argv[1]
@ -861,6 +862,7 @@ namespace tut
set_test_name("'bogus' test");
CaptureLog recorder;
PythonProcessLauncher py(get_test_name(),
"from __future__ import print_function\n"
"print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam("bogus"));
py.mPy = LLProcess::create(py.mParams);
@ -876,6 +878,7 @@ namespace tut
// Replace this test with one or more real 'file' tests when we
// implement 'file' support
PythonProcessLauncher py(get_test_name(),
"from __future__ import print_function\n"
"print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam("file"));
@ -891,6 +894,7 @@ namespace tut
// implement 'tpipe' support
CaptureLog recorder;
PythonProcessLauncher py(get_test_name(),
"from __future__ import print_function\n"
"print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam("tpipe"));
@ -908,6 +912,7 @@ namespace tut
// implement 'npipe' support
CaptureLog recorder;
PythonProcessLauncher py(get_test_name(),
"from __future__ import print_function\n"
"print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam());
@ -984,7 +989,8 @@ namespace tut
{
set_test_name("get*Pipe() validation");
PythonProcessLauncher py(get_test_name(),
"print('this output is expected)'\n");
"from __future__ import print_function\n"
"print('this output is expected')\n");
py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stdin
py.mParams.files.add(LLProcess::FileParam()); // inherit stdout
py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stderr
@ -1004,6 +1010,7 @@ namespace tut
{
set_test_name("talk to stdin/stdout");
PythonProcessLauncher py(get_test_name(),
"from __future__ import print_function\n"
"import sys, time\n"
"print('ok')\n"
"sys.stdout.flush()\n"
@ -1122,6 +1129,7 @@ namespace tut
{
set_test_name("ReadPipe \"eof\" event");
PythonProcessLauncher py(get_test_name(),
"from __future__ import print_function\n"
"print('Hello from Python!')\n");
py.mParams.files.add(LLProcess::FileParam()); // stdin
py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout

View File

@ -0,0 +1,69 @@
/**
* @file threadsafeschedule_test.cpp
* @author Nat Goodspeed
* @date 2021-10-04
* @brief Test for threadsafeschedule.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "threadsafeschedule.h"
// STL headers
// std headers
#include <chrono>
// external library headers
// other Linden headers
#include "../test/lltut.h"
using namespace std::literals::chrono_literals; // ms suffix
using namespace std::literals::string_literals; // s suffix
using Queue = LL::ThreadSafeSchedule<std::string>;
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct threadsafeschedule_data
{
Queue queue;
};
typedef test_group<threadsafeschedule_data> threadsafeschedule_group;
typedef threadsafeschedule_group::object object;
threadsafeschedule_group threadsafeschedulegrp("threadsafeschedule");
template<> template<>
void object::test<1>()
{
set_test_name("push");
// Simply calling push() a few times might result in indeterminate
// delivery order if the resolution of steady_clock is coarser than
// the real time required for each push() call. Explicitly increment
// the timestamp for each one -- but since we're passing explicit
// timestamps, make the queue reorder them.
queue.push(Queue::TimeTuple(Queue::Clock::now() + 200ms, "ghi"));
// Given the various push() overloads, you have to match the type
// exactly: conversions are ambiguous.
queue.push("abc"s);
queue.push(Queue::Clock::now() + 100ms, "def");
queue.close();
auto entry = queue.pop();
ensure_equals("failed to pop first", std::get<0>(entry), "abc"s);
entry = queue.pop();
ensure_equals("failed to pop second", std::get<0>(entry), "def"s);
ensure("queue not closed", queue.isClosed());
ensure("queue prematurely done", ! queue.done());
std::string s;
bool popped = queue.tryPopFor(1s, s);
ensure("failed to pop third", popped);
ensure_equals("third is wrong", s, "ghi"s);
popped = queue.tryPop(s);
ensure("queue not empty", ! popped);
ensure("queue not done", queue.done());
}
} // namespace tut

View File

@ -0,0 +1,47 @@
/**
* @file tuple_test.cpp
* @author Nat Goodspeed
* @date 2021-10-04
* @brief Test for tuple.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "tuple.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "../test/lltut.h"
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct tuple_data
{
};
typedef test_group<tuple_data> tuple_group;
typedef tuple_group::object object;
tuple_group tuplegrp("tuple");
template<> template<>
void object::test<1>()
{
set_test_name("tuple");
std::tuple<std::string, int> tup{ "abc", 17 };
std::tuple<int, std::string, int> ptup{ tuple_cons(34, tup) };
std::tuple<std::string, int> tup2;
int i;
std::tie(i, tup2) = tuple_split(ptup);
ensure_equals("tuple_car() fail", i, 34);
ensure_equals("tuple_cdr() (0) fail", std::get<0>(tup2), "abc");
ensure_equals("tuple_cdr() (1) fail", std::get<1>(tup2), 17);
}
} // namespace tut

View File

@ -0,0 +1,235 @@
/**
* @file workqueue_test.cpp
* @author Nat Goodspeed
* @date 2021-10-07
* @brief Test for workqueue.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "workqueue.h"
// STL headers
// std headers
#include <chrono>
#include <deque>
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "../test/catch_and_store_what_in.h"
#include "llcond.h"
#include "llcoros.h"
#include "lleventcoro.h"
#include "llstring.h"
#include "stringize.h"
using namespace LL;
using namespace std::literals::chrono_literals; // ms suffix
using namespace std::literals::string_literals; // s suffix
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct workqueue_data
{
WorkQueue queue{"queue"};
};
typedef test_group<workqueue_data> workqueue_group;
typedef workqueue_group::object object;
workqueue_group workqueuegrp("workqueue");
template<> template<>
void object::test<1>()
{
set_test_name("name");
ensure_equals("didn't capture name", queue.getKey(), "queue");
ensure("not findable", WorkQueue::getInstance("queue") == queue.getWeak().lock());
WorkQueue q2;
ensure("has no name", LLStringUtil::startsWith(q2.getKey(), "WorkQueue"));
}
template<> template<>
void object::test<2>()
{
set_test_name("post");
bool wasRun{ false };
// We only get away with binding a simple bool because we're running
// the work on the same thread.
queue.post([&wasRun](){ wasRun = true; });
queue.close();
ensure("ran too soon", ! wasRun);
queue.runUntilClose();
ensure("didn't run", wasRun);
}
template<> template<>
void object::test<3>()
{
set_test_name("postEvery");
// record of runs
using Shared = std::deque<WorkQueue::TimePoint>;
// This is an example of how to share data between the originator of
// postEvery(work) and the work item itself, since usually a WorkQueue
// is used to dispatch work to a different thread. Neither of them
// should call any of LLCond's wait methods: you don't want to stall
// either the worker thread or the originating thread (conventionally
// main). Use LLCond or a subclass even if all you want to do is
// signal the work item that it can quit; consider LLOneShotCond.
LLCond<Shared> data;
auto start = WorkQueue::TimePoint::clock::now();
auto interval = 100ms;
queue.postEvery(
interval,
[&data, count = 0]
() mutable
{
// record the timestamp at which this instance is running
data.update_one(
[](Shared& data)
{
data.push_back(WorkQueue::TimePoint::clock::now());
});
// by the 3rd call, return false to stop
return (++count < 3);
});
// no convenient way to close() our queue while we've got a
// postEvery() running, so run until we have exhausted the iterations
// or we time out waiting
for (auto finish = start + 10*interval;
WorkQueue::TimePoint::clock::now() < finish &&
data.get([](const Shared& data){ return data.size(); }) < 3; )
{
queue.runPending();
std::this_thread::sleep_for(interval/10);
}
// Take a copy of the captured deque.
Shared result = data.get();
ensure_equals("called wrong number of times", result.size(), 3);
// postEvery() assumes you want the first call to happen right away.
// Pretend our start time was (interval) earlier than that, to make
// our too early/too late tests uniform for all entries.
start -= interval;
for (size_t i = 0; i < result.size(); ++i)
{
auto diff = result[i] - start;
start += interval;
try
{
ensure(STRINGIZE("call " << i << " too soon"), diff >= interval);
ensure(STRINGIZE("call " << i << " too late"), diff < interval*1.5);
}
catch (const tut::failure&)
{
auto interval_ms = interval / 1ms;
auto diff_ms = diff / 1ms;
std::cerr << "interval " << interval_ms
<< "ms; diff " << diff_ms << "ms" << std::endl;
throw;
}
}
}
template<> template<>
void object::test<4>()
{
set_test_name("postTo");
WorkQueue main("main");
auto qptr = WorkQueue::getInstance("queue");
int result = 0;
main.postTo(
qptr,
[](){ return 17; },
// Note that a postTo() *callback* can safely bind a reference to
// a variable on the invoking thread, because the callback is run
// on the invoking thread. (Of course the bound variable must
// survive until the callback is called.)
[&result](int i){ result = i; });
// this should post the callback to main
qptr->runOne();
// this should run the callback
main.runOne();
ensure_equals("failed to run int callback", result, 17);
std::string alpha;
// postTo() handles arbitrary return types
main.postTo(
qptr,
[](){ return "abc"s; },
[&alpha](const std::string& s){ alpha = s; });
qptr->runPending();
main.runPending();
ensure_equals("failed to run string callback", alpha, "abc");
}
template<> template<>
void object::test<5>()
{
set_test_name("postTo with void return");
WorkQueue main("main");
auto qptr = WorkQueue::getInstance("queue");
std::string observe;
main.postTo(
qptr,
// The ONLY reason we can get away with binding a reference to
// 'observe' in our work callable is because we're directly
// calling qptr->runOne() on this same thread. It would be a
// mistake to do that if some other thread were servicing 'queue'.
[&observe](){ observe = "queue"; },
[&observe](){ observe.append(";main"); });
qptr->runOne();
main.runOne();
ensure_equals("failed to run both lambdas", observe, "queue;main");
}
template<> template<>
void object::test<6>()
{
set_test_name("waitForResult");
std::string stored;
// Try to call waitForResult() on this thread's main coroutine. It
// should throw because the main coroutine must service the queue.
auto what{ catch_what<WorkQueue::Error>(
[this, &stored](){ stored = queue.waitForResult(
[](){ return "should throw"; }); }) };
ensure("lambda should not have run", stored.empty());
ensure_not("waitForResult() should have thrown", what.empty());
ensure(STRINGIZE("should mention waitForResult: " << what),
what.find("waitForResult") != std::string::npos);
// Call waitForResult() on a coroutine, with a string result.
LLCoros::instance().launch(
"waitForResult string",
[this, &stored]()
{ stored = queue.waitForResult(
[](){ return "string result"; }); });
llcoro::suspend();
// Nothing will have happened yet because, even if the coroutine did
// run immediately, all it did was to queue the inner lambda on
// 'queue'. Service it.
queue.runOne();
llcoro::suspend();
ensure_equals("bad waitForResult return", stored, "string result");
// Call waitForResult() on a coroutine, with a void callable.
stored.clear();
bool done = false;
LLCoros::instance().launch(
"waitForResult void",
[this, &stored, &done]()
{
queue.waitForResult([&stored](){ stored = "ran"; });
done = true;
});
llcoro::suspend();
queue.runOne();
llcoro::suspend();
ensure_equals("didn't run coroutine", stored, "ran");
ensure("void waitForResult() didn't return", done);
}
} // namespace tut

View File

@ -0,0 +1,88 @@
/**
* @file threadpool.cpp
* @author Nat Goodspeed
* @date 2021-10-21
* @brief Implementation for threadpool.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "threadpool.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llerror.h"
#include "llevents.h"
#include "stringize.h"
LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity):
mQueue(name, capacity),
mName("ThreadPool:" + name),
mThreadCount(threads)
{}
void LL::ThreadPool::start()
{
for (size_t i = 0; i < mThreadCount; ++i)
{
std::string tname{ stringize(mName, ':', (i+1), '/', mThreadCount) };
mThreads.emplace_back(tname, [this, tname]()
{
LL_PROFILER_SET_THREAD_NAME(tname.c_str());
run(tname);
});
}
// Listen on "LLApp", and when the app is shutting down, close the queue
// and join the workers.
LLEventPumps::instance().obtain("LLApp").listen(
mName,
[this](const LLSD& stat)
{
std::string status(stat["status"]);
if (status != "running")
{
// viewer is starting shutdown -- proclaim the end is nigh!
LL_DEBUGS("ThreadPool") << mName << " saw " << status << LL_ENDL;
close();
}
return false;
});
}
LL::ThreadPool::~ThreadPool()
{
close();
}
void LL::ThreadPool::close()
{
if (! mQueue.isClosed())
{
LL_DEBUGS("ThreadPool") << mName << " closing queue and joining threads" << LL_ENDL;
mQueue.close();
for (auto& pair: mThreads)
{
LL_DEBUGS("ThreadPool") << mName << " waiting on thread " << pair.first << LL_ENDL;
pair.second.join();
}
LL_DEBUGS("ThreadPool") << mName << " shutdown complete" << LL_ENDL;
}
}
void LL::ThreadPool::run(const std::string& name)
{
LL_DEBUGS("ThreadPool") << name << " starting" << LL_ENDL;
run();
LL_DEBUGS("ThreadPool") << name << " stopping" << LL_ENDL;
}
void LL::ThreadPool::run()
{
mQueue.runUntilClose();
}

Some files were not shown because too many files have changed in this diff Show More