feat add tracy profiler
Some checks failed
cpp-template / build (Debug, aarch64-linux-gnu) (push) Failing after 27s
cpp-template / build (Release, mipsel-linux-gnu) (push) Failing after 12m45s
cpp-template / build (Release, host.gcc) (push) Failing after 12m52s
cpp-template / build (Release, arm-linux-gnueabihf) (push) Failing after 12m55s
cpp-template / build (Release, aarch64-linux-gnu) (push) Failing after 13m1s
cpp-template / build (Debug, mipsel-linux-gnu) (push) Failing after 13m6s
cpp-template / build (Debug, host.gcc) (push) Failing after 13m11s
cpp-template / build (Debug, arm-linux-gnueabihf) (push) Failing after 13m19s

This commit is contained in:
tqcq
2025-08-25 15:52:04 +08:00
parent 68b2e7f763
commit cf49554574
183 changed files with 5898 additions and 277856 deletions

View File

@@ -1,4 +1,4 @@
name: sm-rpc
name: cpp-template
on:
push:
pull_request:
@@ -22,5 +22,5 @@ jobs:
- name: build
run: |
mkdir -pv build && cd build
cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAINS}/${{ matrix.toolchain }}.toolchain.cmake
cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAINS}/${{ matrix.toolchain }}.toolchain.cmake -DWITH_EXAMPLES
make -j`nproc`

View File

@@ -11,11 +11,10 @@ if(CMAKE_CXX_COMPILER_ID
endif()
link_libraries(atomic)
option(
WITH_MICROPROFILE
"with MicroProfile"
OFF)
# option(
# WITH_MICROPROFILE
# "with MicroProfile"
# ON)
option(
WITH_EXAMPLES
"with examples"
@@ -41,17 +40,23 @@ check_cxx_source_compiles(
"
HAVE_PTHREAD)
if(NOT
CMAKE_SYSTEM_PROCESSOR
if(CMAKE_SYSTEM_PROCESSOR
MATCHES
"mips*")
link_libraries(atomic)
else()
add_subdirectory(third_party/breakpad)
endif()
add_subdirectory(third_party/tracy)
if(WITH_MICROPROFILE)
add_subdirectory(third_party/microprofile)
set(COMM_LINK_MICROPROFILE ON)
endif()
CPMAddPackage(
NAME tracy
URI https://github.com/wolfpld/tracy
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/tracy
OPTIONS "TRACY_ENABLE ON" "TRACY_ON_DEMAND ON"
FORCE)
CPMAddPackage(
NAME oatpp
@@ -69,11 +74,8 @@ CPMAddPackage(
FORCE)
if(WITH_EXAMPLES)
cc_executable(microprofile_demo SRCS examples/microprofile_demo.cc)
cc_executable(
tracy_demo
SRCS examples/tracy_demo.cc
DEPS PRIVATE TracyClient)
# cc_executable(microprofile_demo SRCS examples/microprofile_demo.cc)
cc_executable(tracy_demo SRCS examples/tracy_demo.cc)
endif()
enable_testing()

View File

@@ -2,12 +2,15 @@ function(comm_link TARGET_NAME)
if(COMM_LINK_ATOMIC)
target_link_libraries(${TARGET_NAME} PUBLIC atomic)
endif()
if(COMM_LINK_PTHREAD)
target_link_libraries(${TARGET_NAME} PUBLIC pthread)
endif()
if(COMM_LINK_MICROPROFILE)
target_link_libraries(${TARGET_NAME} PUBLIC microprofile)
endif()
target_link_libraries(${TARGET_NAME} PUBLIC TracyClient)
# if(COMM_LINK_MICROPROFILE)
# target_link_libraries(${TARGET_NAME} PUBLIC microprofile)
# endif()
endfunction()
# cc_library(name STATIC SRCS a.c b.c DEPS PUBLIC pub_lib PRIVATE pri_lib)

View File

@@ -1,18 +1,35 @@
# Empirical format config, based on observed style guide
# Use this only as an help to fit the surrounding code style - don't reformat whole files at once
---
BasedOnStyle: LLVM
AllowShortIfStatementsOnASingleLine: WithoutElse
BasedOnStyle: Microsoft
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Allman
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
# AllowShortEnumsOnASingleLine: true # Broken for some reason, even in last versions of clang-format... So don't use it or it may change formating in the future.
AllowShortLambdasOnASingleLine: All
BreakConstructorInitializers: BeforeComma
BreakStringLiterals: false
ColumnLimit: 120
SpaceAfterTemplateKeyword: false
AlwaysBreakTemplateDeclarations: Yes
# Allman seems to break lambda formatting for some reason with `ColumnLimit: 0`. See https://github.com/llvm/llvm-project/issues/50275
# Even though it is supposed to have been fixed, issue still remains in 20.1.8. (and is very much present in 18.x which is the one shipped by VS2022 and VSCord clangd as of 2025-07-27)
# Things work fine with `BasedOnStyle: Microsoft` so use that instead
#BreakBeforeBraces: Allman
ColumnLimit: 0
# We'd like to use LeftWithLastLine but it's only available in >=19.x
#AlignEscapedNewlines: LeftWithLastLine
AlignEscapedNewlines: Left
FixNamespaceComments: false
IndentPPDirectives: AfterHash
IndentAccessModifiers: false
AccessModifierOffset: -4
LambdaBodyIndentation: OuterScope
PPIndentWidth: 2
IndentWidth: 4
PointerAlignment: Left
SpaceBeforeParens: Never
SpacesInParentheses: true
TabWidth: 4
AlignTrailingComments:
Kind: Leave

View File

@@ -20,18 +20,22 @@ Checks:
-google-readability-namespace-comments,
-misc-confusable-identifiers,
-misc-no-recursion,
-misc-use-internal-linkage,
-modernize-avoid-c-arrays,
-modernize-deprecated-headers,
-modernize-use-default-member-init,
-modernize-use-designated-initializers,
-modernize-use-trailing-return-type,
-performance-no-int-to-ptr,
-readability-braces-around-statements,
-readability-else-after-return,
-readability-function-cognitive-complexity,
-readability-function-size,
-readability-identifier-length,
-readability-implicit-bool-conversion,
-readability-isolate-declaration,
-readability-magic-numbers,
-readability-math-missing-parentheses,
-readability-qualified-auto,
-readability-uppercase-literal-suffix
'

View File

@@ -19,7 +19,7 @@ jobs:
- name: Setup emscripten
uses: mymindstorm/setup-emsdk@v14
with:
version: 3.1.67
version: 4.0.10
- name: Trust git repo
run: git config --global --add safe.directory '*'
- uses: actions/checkout@v4

View File

@@ -34,3 +34,6 @@ compile_commands.json
profiler/build/wasm/Tracy-release.*
profiler/build/wasm/Tracy-debug.*
profiler/build/wasm/embed.tracy
examples/ToyPathTracer/Windows/TestCpu
examples/ToyPathTracer/Windows/x64
*.user

View File

@@ -33,6 +33,7 @@ else()
endif()
find_package(Threads REQUIRED)
find_package(rocprofiler-sdk PATHS "/opt/rocm/lib/cmake")
set(TRACY_PUBLIC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/public)
@@ -56,6 +57,10 @@ target_link_libraries(
Threads::Threads
${CMAKE_DL_LIBS}
)
if(rocprofiler-sdk_FOUND)
target_compile_definitions(TracyClient PUBLIC TRACY_ROCPROF)
target_link_libraries(TracyClient PUBLIC rocprofiler-sdk::rocprofiler-sdk)
endif()
if(TRACY_Fortran)
add_library(TracyClientF90 ${TRACY_VISIBILITY} "${TRACY_PUBLIC_DIR}/TracyClient.F90")
@@ -142,6 +147,10 @@ set_option(TRACY_VERBOSE "[advanced] Verbose output from the profiler" OFF)
mark_as_advanced(TRACY_VERBOSE)
set_option(TRACY_DEMANGLE "[advanced] Don't use default demangling function - You'll need to provide your own" OFF)
mark_as_advanced(TRACY_DEMANGLE)
if(rocprofiler-sdk_FOUND)
set_option(TRACY_ROCPROF_CALIBRATION "[advanced] Use continuous calibration of the Rocprof GPU time." OFF)
mark_as_advanced(TRACY_ROCPROF_CALIBRATION)
endif()
# handle incompatible combinations
if(TRACY_MANUAL_LIFETIME AND NOT TRACY_DELAYED_INIT)
@@ -209,21 +218,21 @@ set(common_includes
${TRACY_PUBLIC_DIR}/common/TracySocket.hpp
${TRACY_PUBLIC_DIR}/common/TracyStackFrames.hpp
${TRACY_PUBLIC_DIR}/common/TracySystem.hpp
${TRACY_PUBLIC_DIR}/common/TracyUwp.hpp
${TRACY_PUBLIC_DIR}/common/TracyWinFamily.hpp
${TRACY_PUBLIC_DIR}/common/TracyYield.hpp)
install(TARGETS TracyClient
EXPORT TracyConfig
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/$<IF:$<CONFIG:Release>,,$<CONFIG>>
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/$<IF:$<CONFIG:Release>,,$<CONFIG>>
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/$<IF:$<CONFIG:Release>,,$<CONFIG>>
COMPONENT lib)
if(TRACY_Fortran)
install(TARGETS TracyClientF90
EXPORT TracyConfig
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/$<IF:$<CONFIG:Release>,,$<CONFIG>>
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/$<IF:$<CONFIG:Release>,,$<CONFIG>>
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/$<IF:$<CONFIG:Release>,,$<CONFIG>>
COMPONENT lib)
endif()
# Export targets to build tree root

View File

@@ -185,7 +185,7 @@ int main( int argc, char** argv )
}
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
}
printf( "\nQueue delay: %s\nTimer resolution: %s\n", tracy::TimeToString( worker.GetDelay() ), tracy::TimeToString( worker.GetResolution() ) );
printf( "\nTimer resolution: %s\n", tracy::TimeToString( worker.GetResolution() ) );
#ifdef _WIN32
signal( SIGINT, SigInt );

View File

@@ -1,8 +1,8 @@
diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h
index d6ffa5a2d..e48372c64 100644
--- a/backends/imgui_impl_opengl3_loader.h
+++ b/backends/imgui_impl_opengl3_loader.h
@@ -179,6 +179,7 @@ typedef khronos_uint8_t GLubyte;
diff --git i/backends/imgui_impl_opengl3_loader.h w/backends/imgui_impl_opengl3_loader.h
index 4ca0536..a1ff572 100644
--- i/backends/imgui_impl_opengl3_loader.h
+++ w/backends/imgui_impl_opengl3_loader.h
@@ -180,6 +180,7 @@ typedef khronos_uint8_t GLubyte;
#define GL_VERSION 0x1F02
#define GL_EXTENSIONS 0x1F03
#define GL_LINEAR 0x2601
@@ -10,7 +10,7 @@ index d6ffa5a2d..e48372c64 100644
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_TEXTURE_WRAP_S 0x2802
@@ -241,8 +242,10 @@ GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
@@ -244,8 +245,10 @@ GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
#define GL_TEXTURE0 0x84C0
#define GL_ACTIVE_TEXTURE 0x84E0
typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
@@ -21,16 +21,16 @@ index d6ffa5a2d..e48372c64 100644
#endif
#endif /* GL_VERSION_1_3 */
#ifndef GL_VERSION_1_4
@@ -478,7 +481,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
@@ -481,7 +484,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
/* gl3w internal state */
union ImGL3WProcs {
- GL3WglProc ptr[59];
+ GL3WglProc ptr[60];
- GL3WglProc ptr[60];
+ GL3WglProc ptr[61];
struct {
PFNGLACTIVETEXTUREPROC ActiveTexture;
PFNGLATTACHSHADERPROC AttachShader;
@@ -494,6 +497,7 @@ union ImGL3WProcs {
@@ -497,6 +500,7 @@ union ImGL3WProcs {
PFNGLCLEARPROC Clear;
PFNGLCLEARCOLORPROC ClearColor;
PFNGLCOMPILESHADERPROC CompileShader;
@@ -38,7 +38,7 @@ index d6ffa5a2d..e48372c64 100644
PFNGLCREATEPROGRAMPROC CreateProgram;
PFNGLCREATESHADERPROC CreateShader;
PFNGLDELETEBUFFERSPROC DeleteBuffers;
@@ -559,6 +563,7 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs;
@@ -563,6 +567,7 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs;
#define glClear imgl3wProcs.gl.Clear
#define glClearColor imgl3wProcs.gl.ClearColor
#define glCompileShader imgl3wProcs.gl.CompileShader
@@ -46,7 +46,7 @@ index d6ffa5a2d..e48372c64 100644
#define glCreateProgram imgl3wProcs.gl.CreateProgram
#define glCreateShader imgl3wProcs.gl.CreateShader
#define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers
@@ -854,6 +859,7 @@ static const char *proc_names[] = {
@@ -859,6 +864,7 @@ static const char *proc_names[] = {
"glClear",
"glClearColor",
"glCompileShader",

View File

@@ -0,0 +1,39 @@
diff --git i/CMakeLists.txt w/CMakeLists.txt
index 8efec25..c1d101e 100644
--- i/CMakeLists.txt
+++ w/CMakeLists.txt
@@ -17,7 +17,7 @@
# @date Consult git log.
##############################################################################
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.10)
set(LIB_NAME tidy)
set(LIBTIDY_DESCRIPTION "${LIB_NAME} - HTML syntax checker")
@@ -528,6 +528,7 @@ if (UNIX AND SUPPORT_CONSOLE_APP)
# Run the built EXE to generate xml output .
add_custom_command(
+ POST_BUILD
TARGET man
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME} -xml-help > ${TIDYHELP}
COMMENT "Generate ${TIDYHELP}"
@@ -536,6 +537,7 @@ if (UNIX AND SUPPORT_CONSOLE_APP)
# Run the built EXE to generate more xml output.
add_custom_command(
+ POST_BUILD
TARGET man
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME} -xml-config > ${TIDYCONFIG}
COMMENT "Generate ${TIDYCONFIG}"
@@ -544,8 +546,8 @@ if (UNIX AND SUPPORT_CONSOLE_APP)
# Run xsltproc to generate the install files.
add_custom_command(
+ POST_BUILD
TARGET man
- DEPENDS ${TIDYHELP}
COMMAND xsltproc ARGS ${TIDY1XSL} ${TIDYHELP} > ${CMAKE_CURRENT_BINARY_DIR}/${TIDY_MANFILE}
COMMENT "Generate ${TIDY_MANFILE}"
VERBATIM

View File

@@ -11,6 +11,8 @@ include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake)
option(DOWNLOAD_CAPSTONE "Force download capstone" ON)
option(DOWNLOAD_GLFW "Force download glfw" OFF)
option(DOWNLOAD_FREETYPE "Force download freetype" OFF)
option(DOWNLOAD_LIBCURL "Force download libcURL" OFF)
option(DOWNLOAD_PUGIXML "Force download pugixml" OFF)
# capstone
@@ -134,7 +136,7 @@ target_include_directories(TracyGetOpt PUBLIC ${GETOPT_DIR})
CPMAddPackage(
NAME ImGui
GITHUB_REPOSITORY ocornut/imgui
GIT_TAG v1.91.9b-docking
GIT_TAG v1.92.1-docking
DOWNLOAD_ONLY TRUE
PATCHES
"${CMAKE_CURRENT_LIST_DIR}/imgui-emscripten.patch"
@@ -157,9 +159,10 @@ add_library(TracyImGui STATIC EXCLUDE_FROM_ALL ${IMGUI_SOURCES})
target_include_directories(TracyImGui PUBLIC ${ImGui_SOURCE_DIR})
target_link_libraries(TracyImGui PUBLIC TracyFreetype)
target_compile_definitions(TracyImGui PRIVATE "IMGUI_ENABLE_FREETYPE")
#target_compile_definitions(TracyImGui PUBLIC "IMGUI_DISABLE_OBSOLETE_FUNCTIONS")
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(TracyImGui PRIVATE "IMGUI_DISABLE_DEBUG_TOOLS")
target_compile_definitions(TracyImGui PRIVATE "IMGUI_DISABLE_DEBUG_TOOLS" "IMGUI_DISABLE_DEMO_WINDOWS")
endif()
# NFD
@@ -191,3 +194,103 @@ CPMAddPackage(
"${CMAKE_CURRENT_LIST_DIR}/ppqsort-nodebug.patch"
EXCLUDE_FROM_ALL TRUE
)
# json
CPMAddPackage(
NAME json
GITHUB_REPOSITORY nlohmann/json
GIT_TAG v3.12.0
EXCLUDE_FROM_ALL TRUE
)
# md4c
CPMAddPackage(
NAME md4c
GITHUB_REPOSITORY mity/md4c
GIT_TAG release-0.5.2
EXCLUDE_FROM_ALL TRUE
)
if(NOT EMSCRIPTEN)
# base64
set(BUILD_SHARED_LIBS_SAVE ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF)
CPMAddPackage(
NAME base64
GITHUB_REPOSITORY aklomp/base64
GIT_TAG v0.5.2
OPTIONS
"BASE64_BUILD_CLI OFF"
"BASE64_WITH_OpenMP OFF"
EXCLUDE_FROM_ALL TRUE
)
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVE})
# tidy
CPMAddPackage(
NAME tidy
GITHUB_REPOSITORY htacg/tidy-html5
GIT_TAG 5.8.0
PATCHES
"${CMAKE_CURRENT_LIST_DIR}/tidy-cmake.patch"
EXCLUDE_FROM_ALL TRUE
)
# usearch
CPMAddPackage(
NAME usearch
GITHUB_REPOSITORY unum-cloud/usearch
GIT_TAG v2.19.1
EXCLUDE_FROM_ALL TRUE
)
# pugixml
pkg_check_modules(PUGIXML pugixml)
if (PUGIXML_FOUND AND NOT DOWNLOAD_PUGIXML)
add_library(TracyPugixml INTERFACE)
target_include_directories(TracyPugixml INTERFACE ${PUGIXML_INCLUDE_DIRS})
target_link_libraries(TracyPugixml INTERFACE ${PUGIXML_LINK_LIBRARIES})
else()
CPMAddPackage(
NAME pugixml
GITHUB_REPOSITORY zeux/pugixml
GIT_TAG v1.15
EXCLUDE_FROM_ALL TRUE
)
add_library(TracyPugixml INTERFACE)
target_link_libraries(TracyPugixml INTERFACE pugixml)
endif()
# libcurl
pkg_check_modules(LIBCURL libcurl)
if (LIBCURL_FOUND AND NOT DOWNLOAD_LIBCURL)
add_library(TracyLibcurl INTERFACE)
target_include_directories(TracyLibcurl INTERFACE ${LIBCURL_INCLUDE_DIRS})
target_link_libraries(TracyLibcurl INTERFACE ${LIBCURL_LINK_LIBRARIES})
else()
CPMAddPackage(
NAME libcurl
GITHUB_REPOSITORY curl/curl
GIT_TAG curl-8_14_1
OPTIONS
"BUILD_STATIC_LIBS ON"
"BUILD_SHARED_LIBS OFF"
"HTTP_ONLY ON"
"CURL_ZSTD OFF"
"CURL_USE_LIBPSL OFF"
EXCLUDE_FROM_ALL TRUE
)
add_library(TracyLibcurl INTERFACE)
target_link_libraries(TracyLibcurl INTERFACE libcurl_static)
target_include_directories(TracyLibcurl INTERFACE ${libcurl_SOURCE_DIR}/include)
endif()
endif()

View File

@@ -20,7 +20,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/../cmake/server.cmake)
add_executable(tracy-import-chrome
src/import-chrome.cpp
)
target_link_libraries(tracy-import-chrome PRIVATE TracyServer)
target_link_libraries(tracy-import-chrome PRIVATE TracyServer nlohmann_json::nlohmann_json)
add_executable(tracy-import-fuchsia
src/import-fuchsia.cpp

View File

@@ -3,6 +3,7 @@
#endif
#include <fstream>
#include <nlohmann/json.hpp>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -18,8 +19,6 @@
# define stat64 stat
#endif
#include "json.hpp"
#include "../../server/TracyFileWrite.hpp"
#include "../../server/TracyMmap.hpp"
#include "../../server/TracyWorker.hpp"

File diff suppressed because it is too large Load Diff

5
third_party/tracy/manual/filter.lua vendored Normal file
View File

@@ -0,0 +1,5 @@
function Link(el)
el.attributes['reference-type'] = nil
el.attributes['reference'] = nil
return el
end

26
third_party/tracy/manual/latex2md.sh vendored Normal file
View File

@@ -0,0 +1,26 @@
#!/bin/sh
cp -f tracy.tex _tmp.tex
sed -i -e 's@\\menu\[,\]@@g' _tmp.tex
sed -i -e 's@\\keys@@g' _tmp.tex
sed -i -e 's@\\ctrl@Ctrl@g' _tmp.tex
sed -i -e 's@\\shift@Shift@g' _tmp.tex
sed -i -e 's@\\Alt@Alt@g' _tmp.tex
sed -i -e 's@\\del@Delete@g' _tmp.tex
sed -i -e 's@\\fa\([a-zA-Z]*\)@(\1~icon)@g' _tmp.tex
sed -i -e 's@\\LMB{}~@@g' _tmp.tex
sed -i -e 's@\\MMB{}~@@g' _tmp.tex
sed -i -e 's@\\RMB{}~@@g' _tmp.tex
sed -i -e 's@\\Scroll{}~@@g' _tmp.tex
sed -i -e 's@\\nameref{quicklook}@A quick look at Tracy Profiler@g' _tmp.tex
sed -i -e 's@\\nameref{firststeps}@First steps@g' _tmp.tex
sed -i -e 's@\\nameref{client}@Client markup@g' _tmp.tex
sed -i -e 's@\\nameref{capturing}@Capturing the data@g' _tmp.tex
sed -i -e 's@\\nameref{analyzingdata}@Analyzing captured data@g' _tmp.tex
sed -i -e 's@\\nameref{csvexport}@Exporting zone statistics to CSV@g' _tmp.tex
sed -i -e 's@\\nameref{importingdata}@Importing external profiling data@g' _tmp.tex
sed -i -e 's@\\nameref{configurationfiles}@Configuration files@g' _tmp.tex
pandoc --wrap=none --reference-location=block --number-sections -L filter.lua -s _tmp.tex -o tracy.md
rm -f _tmp.tex

4249
third_party/tracy/manual/tracy.md vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,7 @@
\usepackage[euler]{textgreek}
\usepackage{nameref}
\usepackage{diagbox}
\usepackage{csquotes}
\usepackage[hmarginratio=1:1,top=32mm,columnsep=20pt]{geometry} % Document margins
\geometry{a4paper,textwidth=6.5in,hmarginratio=1:1,
@@ -1640,7 +1641,7 @@ To enable calibrated context, replace the macro \texttt{TracyVkContext} with \te
\subparagraph{Using Vulkan 1.2 features}
Vulkan 1.2 and \texttt{VK\_EXT\_host\_query\_reset} provide mechanics to reset the query pool without the need of a command buffer. By using \texttt{TracyVkContextHostCalibrated} you can make use of this feature. It only requires a function pointer to \texttt{vkResetQueryPool} in addition to the ones required for \texttt{TracyVkContextCalibrated} instead of the VkQueue and VkCommandBuffer handles.
Vulkan 1.2 and \texttt{VK\_EXT\_host\_query\_reset} provide mechanics to reset the query pool without the need of a command buffer. By using \texttt{TracyVkContextHostCalibrated} and \texttt{TracyVkCollectHost}, you can make use of this feature. It only requires a function pointer to \texttt{vkResetQueryPool} in addition to the ones required for \texttt{TracyVkContextCalibrated} instead of the VkQueue and VkCommandBuffer handles.
However, using this feature requires the physical device to have calibrated device and host time domains. In addition to \texttt{VK\_TIME\_DOMAIN\_DEVICE\_EXT}, \texttt{vkGetPhysicalDeviceCalibrateableTimeDomainsEXT} will have to additionally return either \texttt{VK\_TIME\_DOMAIN\_CLOCK\_MONOTONIC\_RAW\_EXT} or \texttt{VK\_TIME\_DOMAIN\_QUERY\_PERFORMANCE\_COUNTER\_EXT} for Unix and Windows, respectively. If this is not the case, you will need to use \texttt{TracyVkContextCalibrated} or \texttt{TracyVkContext} macro instead.
@@ -1688,7 +1689,7 @@ OpenCL support is achieved by including the \texttt{public/tracy/TracyOpenCL.hpp
To mark an OpenCL zone one must make sure that a valid OpenCL \texttt{cl\_event} object is available. The event will be the object that Tracy will use to query profiling information from the OpenCL driver. For this to work, you must create all OpenCL queues with the \texttt{CL\_QUEUE\_PROFILING\_ENABLE} property.
OpenCL zones can be created with the \texttt{TracyCLZone(ctx, name)} where \texttt{name} will usually be a descriptive name for the operation represented by the \texttt{cl\_event}. Within the scope of the zone, you must call \texttt{TracyCLSetEvent(event)} for the event to be registered in Tracy.
OpenCL zones can be created with the \texttt{TracyCLZone(ctx, name)} where \texttt{name} will usually be a descriptive name for the operation represented by the \texttt{cl\_event}. Within the scope of the zone, you must call \texttt{TracyCLZoneSetEvent(event)} for the event to be registered in Tracy.
Similar to Vulkan and OpenGL, you also need to periodically collect the OpenCL events using the \texttt{TracyCLCollect(ctx)} macro. An excellent place to perform this operation is after a \texttt{clFinish} since this will ensure that any previously queued OpenCL commands will have finished by this point.
@@ -1706,6 +1707,35 @@ Unlike other GPU backends in Tracy, there is no need to call \texttt{TracyCUDACo
To stop profiling, call the \texttt{TracyCUDAStopProfiling(ctx)} macro.
\subsubsection{ROCm}
On Linux, if rocprofiler-sdk is installed, tracy can automatically trace GPU dispatches and collect
performance counter values. If CMake can't find rocprofiler-sdk, you can set the CMake variable
\texttt{rocprofiler-sdk\_DIR} to point it at the correct module directory. Use the
\texttt{TRACY\_ROCPROF\_COUNTERS} environment variable with the desired counters separated by commas
to control what values are collected. The results will appear for each dispatch in the tool tip and
zone detail window. Results are summed across dimensions. You can get a list of the counters
available for your hardware with this command:
\begin{lstlisting}[language=sh]
rocprofv3 -L
\end{lstlisting}
\subparagraph{Troubleshooting}
\begin{itemize}
\item If you are taking very long captures, you may see drift between the GPU and
CPU timelines. This may be mitigated by setting the CMake variable
\texttt{TRACY\_ROCPROF\_CALIBRATION}, which will refresh the time synchronization about every
second.
\item The timeline drift may also be affected by network time synchronization, in which case the
drift will be reduced by disabling that, with the advantage that there is no application performance
cost.
\item On some GPUs, you will need to change the the performance level to see non-zero results from
some counters. Use this command:
\begin{lstlisting}[language=sh]
sudo amd-smi set -g 0 -l stable_std
\end{lstlisting}
\end{itemize}
\subsubsection{Multiple zones in one scope}
Putting more than one GPU zone macro in a single scope features the same issue as with the \texttt{ZoneScoped} macros, described in section~\ref{multizone} (but this time the variable name is \texttt{\_\_\_tracy\_gpu\_zone}).
@@ -2362,37 +2392,30 @@ Please not the use of ids as way to cope with the need for unique pointers for c
\subsubsection{Building the Python package}
To build the Python package, you will need to use the CMake build system to compile the Tracy-Client.
The CMake option \texttt{-D TRACY\_CLIENT\_PYTHON=ON} is used to enable the generation of the Python bindings in conjunction with a mandatory creation of a shared Tracy-Client library via one of the CMake options \texttt{-D BUILD\_SHARED\_LIBS=ON} or \texttt{-D DEFAULT\_STATIC=OFF}.
The following other variables are available in addition:
\begin{itemize}
\item \texttt{EXTERNAL\_PYBIND11} --- Can be used to disable the download of pybind11 when Tracy is embedded in another CMake project that already uses pybind11.
\item \texttt{TRACY\_CLIENT\_PYTHON\_TARGET} --- Optional directory to copy Tracy Python bindings to when Tracy is embedded in another CMake project.
\item \texttt{BUFFER\_SIZE} --- The size of the global pointer buffer (defaults to 128) for naming Tracy profiling entities like frame marks, plots, and memory locations.
\item \texttt{NAME\_LENGTH} --- The maximum length (defaults to 128) of a name stored in the global pointer buffer.
\end{itemize}
Be aware that the memory allocated by this buffer is global and is not freed, see section~\ref{uniquepointers}.
See below for example steps to build the Python bindings using CMake:
\begin{lstlisting}
mkdir build
cd build
cmake -DTRACY_STATIC=OFF -DTRACY_CLIENT_PYTHON=ON ../
make -j$(nproc)
\end{lstlisting}
Once this has finished building the Python package can be built as follows:
To build the Python package, run the following commands:
\begin{lstlisting}
cd ../python
python3 setup.py bdist_wheel
pip wheel .
\end{lstlisting}
The created package will be in the folder \texttt{python/dist}.
This will create a wheel package in the \texttt{python} folder.
Please note that this requires CMake and a C++ compiler installed on the system, as the Tracy-Client library is built in the background.
You can pass additional CMake options to the package build to configure the Tracy-Client library:
\begin{lstlisting}
pip wheel . --config-settings cmake.define.TRACY_ENABLE=OFF
\end{lstlisting}
The following additional CMake options are available when building the Python package:
\begin{itemize}
\item \texttt{BUFFER\_SIZE} --- The size of the global pointer buffer (defaults to 128) for naming Tracy profiling entities like frame marks, plots, and memory locations.
\item \texttt{NAME\_LENGTH} --- The maximum length (defaults to 128) of a name stored in the global pointer buffer.
\item \texttt{EXTERNAL\_PYBIND11} --- Can be used to disable the download of pybind11 when Tracy is embedded in another CMake project that already uses pybind11.
\end{itemize}
Be aware that the memory allocated by this buffer is global and is not freed, see section~\ref{uniquepointers}.
\subsection{Fortran API}
\label{fortranapi}
@@ -2679,9 +2702,13 @@ You may disable context switch data capture by adding the \texttt{TRACY\_NO\_CON
Tracy may discover CPU topology data to provide further information about program performance characteristics. It is handy when combined with context switch information (section~\ref{contextswitches}).
In essence, the topology information gives you context about what any given \emph{logical CPU} really is and how it relates to other logical CPUs. The topology hierarchy consists of packages, cores, and threads.
In essence, the topology information gives you context about what any given \emph{logical CPU} really is and how it relates to other logical CPUs. The topology hierarchy consists of packages, dies, cores, and threads.
Packages contain cores and shared resources, such as memory controller, L3 cache, etc. A store-bought CPU is an example of a package. While you may think that multi-package configurations would be a domain of servers, they are actually quite common in the mobile devices world, with many platforms using the \emph{big.LITTLE} arrangement of two packages in one silicon chip.
Packages contain cores and shared resources, such as a memory controller or L3 cache. They also include a common connector to access peripheral hardware and receive power. An example of a package is a store-bought CPU.
Historically, a CPU would contain all its cores, controllers, and caches in a single piece of semiconductor called a die. More advanced CPU designs that have recently appeared may split the available cores across two or more dies. An additional die may be invisible to the user and facilitate communication between the cores. This is an important detail to consider when profiling because the latency of core interactions will differ between cores that are physically close together on a single die versus cores that need to communicate through die interconnects.
While you may think that multi-package configurations would be a domain of servers, they are actually quite common in the mobile devices world, with many platforms using the \emph{big.LITTLE} arrangement of two packages in one silicon chip.
Cores contain at least one thread and shared resources: execution units, L1 and L2 cache, etc.
@@ -2708,6 +2735,8 @@ By default, sampling is performed at 8 kHz frequency on Windows (the maximum pos
Call stack sampling may be disabled by using the \texttt{TRACY\_NO\_SAMPLING} define.
When enabled, by default, sampling starts at the beginning of the application and ends with it. You can instead have programmatic (manual) control over when sampling should begin and end by defining \texttt{TRACY\_SAMPLING\_PROFILER\_MANUAL\_START} when compiling \texttt{TracyClient.cpp}. Use \texttt{tracy::BeginSamplingProfiling()} and \texttt{tracy::EndSamplingProfiling()} to control it. There are C interfaces for it as well: \texttt{TracyCBeginSamplingProfiling()} and \texttt{TracyCEndSamplingProfiling()}.
\begin{bclogo}[
noborder=true,
couleur=black!5,
@@ -2852,23 +2881,22 @@ You can capture a trace using a command line utility contained in the \texttt{ca
If no client is running at the given address, the server will wait until it can make a connection. During the capture, the utility will display the following information:
\begin{verbatim}
% ./capture -a 127.0.0.1 -o trace
% ./tracy-capture -a 127.0.0.1 -o trace
Connecting to 127.0.0.1:8086...
Queue delay: 5 ns
Timer resolution: 3 ns
1.33 Mbps / 40.4% = 3.29 Mbps | Net: 64.42 MB | Mem: 283.03 MB | Time: 10.6 s
\end{verbatim}
The \emph{queue delay} and \emph{timer resolution} parameters are calibration results of timers used by the client. The following line is a status bar, which displays: network connection speed, connection compression ratio, and the resulting uncompressed data rate; the total amount of data transferred over the network; memory usage of the capture utility; time extent of the captured data.
The \emph{timer resolution} parameter shows the calibration results of timers used by the client. The following line is a status bar, which displays: network connection speed, connection compression ratio, and the resulting uncompressed data rate; the total amount of data transferred over the network; memory usage of the capture utility; time extent of the captured data.
You can disconnect from the client and save the captured trace by pressing \keys{\ctrl + C}. If you prefer to disconnect after a fixed time, use the \texttt{-s seconds} parameter.
\subsection{Interactive profiling}
\label{interactiveprofiling}
If you want to look at the profile data in real-time (or load a saved trace file), you can use the data analysis utility contained in the \texttt{profiler} directory. After starting the application, you will be greeted with a welcome dialog (figure~\ref{welcomedialog}), presenting a bunch of useful links (\faBook{}~\emph{User manual}, \faGlobeAmericas{}~\emph{Web}, \faComment~\emph{Join chat} and \faHeart{}~\emph{Sponsor}). The \faGlobeAmericas{}~\emph{Web} button opens a drop-down list with links to the profiler's \emph{\faHome{}~Home page} and a bunch of \emph{\faVideo{}~Feature videos}.
If you want to look at the profile data in real-time (or load a saved trace file), you can use the data analysis utility \texttt{tracy-profiler} contained in the \texttt{profiler} directory. After starting the application, you will be greeted with a welcome dialog (figure~\ref{welcomedialog}), presenting a bunch of useful links (\faBook{}~\emph{User manual}, \faGlobeAmericas{}~\emph{Web}, \faComment~\emph{Join chat} and \faHeart{}~\emph{Sponsor}). The \faGlobeAmericas{}~\emph{Web} button opens a drop-down list with links to the profiler's \emph{\faHome{}~Home page} and a bunch of \emph{\faVideo{}~Feature videos}.
The \emph{\faWrench{}~Wrench} button opens the about dialog, which also contains a number of global settings you may want to tweak.
The \emph{\faWrench{}~Wrench} button opens the about dialog, which also contains a number of global settings you may want to tweak (section~\ref{aboutwindow}).
The client \emph{address entry} field and the \faWifi{}~\emph{Connect} button are used to connect to a running client\footnote{Note that a custom port may be provided here, for example by entering '127.0.0.1:1234'.}. You can use the connection history button~\faCaretDown{} to display a list of commonly used targets, from which you can quickly select an address. You can remove entries from this list by hovering the \faMousePointer{}~mouse cursor over an entry and pressing the \keys{\del} button on the keyboard.
@@ -2905,6 +2933,28 @@ Both connecting to a client and opening a saved trace will present you with the
Once connected to a client \keys{\ctrl + \shift + \Alt + R} can be used to quickly discard any captured data and reconnect to a client at the same address.
\subsubsection{About window}
\label{aboutwindow}
The About window displays the profiler version and the Git SHA identifier of the build, as well as some additional information.
You can also adjust some settings that affect global profiler behavior in this window. These settings are accessible by expanding the \emph{\faToolbox{}~Global settings} node. The following options are available:
\begin{itemize}
\item \emph{Threaded rendering} -- This controls whether the profiler UI uses multithreaded rendering. Since the profiler needs to quickly navigate large amounts of data, it spends a lot of time waiting for memory accesses to be resolved. Multithreading enables multiple simultaneous memory reads, which significantly reduces the impact of memory access latency. However, this may result in higher CPU usage, which could interfere with the application you are profiling.
\item \emph{Reduce render rate when focus is lost} -- This throttles the profiler window refresh rate to 20 FPS when the window does not have focus.
\item \emph{Target FPS} -- Sets the default \emph{target FPS} value for the \emph{Frame time graph}. See sections~\ref{frametimegraph} and~\ref{options} for more information. Not related to the profiler window refresh rate.
\item \emph{Zone colors} -- Sets the default zone coloring preset used in new traces. See section~\ref{options} for more information.
\item \emph{Zone name shortening} -- Sets the default zone name shortening behavior used in new traces. See section~\ref{options} for more information.
\item \emph{Scroll multipliers} -- Allows you to fine-tune the sensitivity of the horizontal and vertical scroll in the timeline. The default values ($1.0$) are an attempt at the best possible settings, but differences in hardware manufacturers, platform implementations, and user expectations may require adjustments.
\item \emph{Memory limit} -- When enabled, profiler will stop recording data when memory usage exceeds the specified percentage of the total system memory. This mechanism does not measure the current system memory usage or limits. The upper value is not capped, as you may use swap. See section~\ref{memoryusage} for more information.
\item \emph{Enable achievements} -- Enables achievements system, accessed through the~\faStar{}~icon in the bottom right corner of the profiler window. It is essentially a gamified tutorial system designed to teach new users how to use the profiler.
\item \emph{Save UI scale} -- Determines whether the UI scale set by the user should be saved between sessions. This setting is not related to DPI scaling.
\item \emph{Enable Tracy Assist} -- Controls whether the automated assistant features (based on large language models) are available through the Profiler UI. See section~\ref{tracyassist} for more details.
\end{itemize}
\subsubsection{Connection information pop-up}
\label{connectionpopup}
@@ -2946,6 +2996,7 @@ Tracy network bandwidth requirements depend on the amount of data collection the
The maximum attainable connection speed is determined by the ability of the client to provide data and the ability of the server to process the received data. In an extreme conditions test performed on an i7~8700K, the maximum transfer rate peaked at 950~Mbps. In each second, the profiler could process 27~million zones and consume 1~GB of RAM.
\subsection{Memory usage}
\label{memoryusage}
The captured data is stored in RAM and only written to the disk when the capture finishes. This can result in memory exhaustion when you capture massive amounts of profile data or even in typical usage situations when the capture is performed over a long time. Therefore, the recommended usage pattern is to perform moderate instrumentation of the client code and limit capture time to the strict necessity.
@@ -2960,7 +3011,7 @@ Each new release of Tracy changes the internal format of trace files. While ther
To use it, you will need to provide the input file and the output file. The program will print a short summary when it finishes, with information about trace file versions, their respective sizes and the output trace file compression ratio:
\begin{verbatim}
% ./update old.tracy new.tracy
% ./tracy-update old.tracy new.tracy
old.tracy (0.3.0) {916.4 MB} -> new.tracy (0.4.0) {349.4 MB, 31.53%} 9.7 s, 38.13% change
\end{verbatim}
@@ -3181,19 +3232,20 @@ The main profiler window is split into three sections, as seen in figure~\ref{ma
\begin{figure}[h]
\centering\begin{tikzpicture}
\draw (0, 0) rectangle (16.1, -5.5);
\draw[pattern=crosshatch dots] (0, 0) rectangle+(16.1, 0.3);
\draw (0, 0) rectangle (16.2, -5.5);
\draw[pattern=crosshatch dots] (0, 0) rectangle+(16.2, 0.3);
\draw[rounded corners=5pt] (0.1, -0.1) rectangle+(0.5, -0.5) node [midway] {\faPowerOff};
\draw[rounded corners=5pt] (0.7, -0.1) rectangle+(0.5, -0.5) node [midway] {\faCog{}};
\draw[rounded corners=5pt] (1.3, -0.1) rectangle+(2.2, -0.5) node [midway] {\faTags{} Messages};
\draw[rounded corners=5pt] (3.6, -0.1) rectangle+(1.5, -0.5) node [midway] {\faSearch{} Find};
\draw[rounded corners=5pt] (5.2, -0.1) rectangle+(2, -0.5) node [midway] {\faSortAmountUp{} Statistics};
\draw[rounded corners=5pt] (7.3, -0.1) rectangle+(1.6, -0.5) node [midway] {\faFire{} Flame};
\draw[rounded corners=5pt] (9.0, -0.1) rectangle+(2.2, -0.5) node [midway] {\faMemory{} Memory};
\draw[rounded corners=5pt] (11.3, -0.1) rectangle+(2.1, -0.5) node [midway] {\faBalanceScale{} Compare};
\draw[rounded corners=5pt] (13.5, -0.1) rectangle+(1.3, -0.5) node [midway] {\faFingerprint{} Info};
\draw[rounded corners=5pt] (14.9, -0.1) rectangle+(0.5, -0.5) node [midway] {\faTools{}};
\draw[rounded corners=5pt] (15.5, -0.1) rectangle+(0.5, -0.5) node [midway] {\faSearchPlus{}};
\draw[rounded corners=5pt] (3.6, -0.1) rectangle+(1.3, -0.5) node [midway] {\faSearch{} Find};
\draw[rounded corners=5pt] (5.0, -0.1) rectangle+(2, -0.5) node [midway] {\faSortAmountUp{} Statistics};
\draw[rounded corners=5pt] (7.1, -0.1) rectangle+(1.5, -0.5) node [midway] {\faFire{} Flame};
\draw[rounded corners=5pt] (8.7, -0.1) rectangle+(2.1, -0.5) node [midway] {\faMemory{} Memory};
\draw[rounded corners=5pt] (10.9, -0.1) rectangle+(2.1, -0.5) node [midway] {\faBalanceScale{} Compare};
\draw[rounded corners=5pt] (13.1, -0.1) rectangle+(1.2, -0.5) node [midway] {\faFingerprint{} Info};
\draw[rounded corners=5pt] (14.4, -0.1) rectangle+(0.5, -0.5) node [midway] {\faTools{}};
\draw[rounded corners=5pt] (15.0, -0.1) rectangle+(0.5, -0.5) node [midway] {\faSearchPlus{}};
\draw[rounded corners=5pt] (15.6, -0.1) rectangle+(0.5, -0.5) node [midway] {\faRobot{}};
\draw[rounded corners=5pt] (0.1, -0.7) rectangle+(0.4, -0.5) node [midway] {\faCaretLeft};
\draw (0.6, -0.7) node[anchor=north west] {Frames: 364};
\draw[rounded corners=5pt] (2.8, -0.7) rectangle+(0.4, -0.5) node [midway] {\faCaretRight};
@@ -3201,8 +3253,8 @@ The main profiler window is split into three sections, as seen in figure~\ref{ma
\draw (4, -0.65) node[anchor=north west] {\faEye~52.7 ms \hspace{5pt} \faDatabase~6.06 s \hspace{5pt} \faMemory~195.2 MB};
\draw[dashed] (10.1, -0.75) rectangle+(3.2, -0.4) node[midway] {Notification area};
\draw (0.1, -1.3) rectangle+(15.9, -1) node [midway] {Frame time graph};
\draw (0.1, -2.4) rectangle+(15.9, -3) node [midway] {Timeline view};
\draw (0.1, -1.3) rectangle+(16.0, -1) node [midway] {Frame time graph};
\draw (0.1, -2.4) rectangle+(16.0, -3) node [midway] {Timeline view};
\end{tikzpicture}
\caption{Main profiler window. Note that this manual has split the top line of buttons into two rows.}
\label{mainwindow}
@@ -3236,11 +3288,12 @@ The control menu (top row of buttons) provides access to various profiler featur
\item \emph{\faHourglassHalf{}~Wait stacks} -- If sampling was performed, an option to display wait stacks may be available. See chapter~\ref{waitstacks} for more details.
\end{itemize}
\item \emph{\faSearchPlus{}~Display scale} -- Enables run-time resizing of the displayed content. This may be useful in environments with potentially reduced visibility, e.g. during a presentation. Note that this setting is independent to the UI scaling coming from the system DPI settings. The scale will be preserved across multiple profiler sessions if the \emph{Save UI scale} option is selected in global settings.
\item \emph{\faRobot{}~Tracy Assist} -- Shows the automated assistant chat window (section~\ref{tracyassist}). Only available if enabled in global settings (section~\ref{aboutwindow}).
\end{itemize}
The frame information block\footnote{Visible only if frame instrumentation was included in the capture.} consists of four elements: the current frame set name along with the number of captured frames (click on it with the \LMB{}~left mouse button to go to a specified frame), the two navigational buttons \faCaretLeft{} and \faCaretRight{}, which allow you to focus the timeline view on the previous or next frame, and the frame set selection button \faCaretDown{}, which is used to switch to another frame set\footnote{See section~\ref{framesets} for another way to change the active frame set.}. For more information about marking frames, see section~\ref{markingframes}.
The following three items show the \emph{\faEye{}~view time range}, the \emph{\faDatabase{}~time span} of the whole capture (clicking on it with the \MMB{} middle mouse button will set the view range to the entire capture), and the \emph{\faMemory{}~memory usage} of the profiler.
The following three items show the \emph{\faEye{}~view time range}, the \emph{\faDatabase{}~time span} of the whole capture (clicking on it with the \MMB{}~middle mouse button will set the view range to the entire capture), and the \emph{\faMemory{}~memory usage} of the profiler.
\paragraph{Notification area}
@@ -3412,7 +3465,7 @@ In figure~\ref{framesetsfig} we can see the fully described frames~312 and 347.
You can also see frame separators are projected down to the rest of the timeline view. Note that only the separators for the currently selected frame set are displayed. You can make a frame set active by clicking the \LMB{}~left mouse button on a frame set row you want to select (also see section~\ref{controlmenu}).
Clicking the \MMB{} middle mouse button on a frame will zoom the view to the extent of the frame.
Clicking the \MMB{}~middle mouse button on a frame will zoom the view to the extent of the frame.
If a frame has an associated frame image (see chapter~\ref{frameimages}), you can hold the \keys{\ctrl} key and click the \LMB{}~left mouse button on the frame to open the frame image playback window (see chapter~\ref{playback}) and set the playback to the selected frame.
@@ -3458,6 +3511,14 @@ You will find the zones with locks and their associated threads on this combined
\draw(7.5, -0.5) rectangle+(6.5, -0.5) node[midway] {Render};
\draw[densely dotted,ultra thick,,color=lightgray] (0.11, -0.25) -- (0.11, -1.75);
\draw(0.11, -0.25) node[circle,draw,fill,color=lightgray,inner sep=0pt,minimum size=3.5] {};
\draw(0.11, -0.75) node[circle,draw,fill,color=lightgray,inner sep=0pt,minimum size=3.5] {};
\draw(0.11, -1.25) node[circle,draw,fill,color=lightgray,inner sep=0pt,minimum size=3.5] {};
\draw(0.11, -1.75) node[circle,draw,fill,color=lightgray,inner sep=0pt,minimum size=3.5] {};
\draw(0, -2.5) node[anchor=north west] {Physics lock};
\draw[pattern=crosshatch dots] (3.1, -2.5) rectangle+(2.5, -0.5);
@@ -3497,6 +3558,8 @@ Labels accompanied by the \faCaretDown{}~symbol can be collapsed out of the view
\item \emph{\faEyeSlash{}~Hide} -- Hides the label along with the content associated to it. To make the label visible again, you must find it in the options menu (section~\ref{options}).
\end{itemize}
Under the \faCaretDown{}~symbol are a series of points that allow to limit the depth of the zones displayed. Hover the~\faMousePointer{}~mouse pointer over a circle to display a line visualizing the cutting point, then click the \MMB{}~middle mouse button to apply or remove a zone depth limit.
\subparagraph{Zones}
In an example in figure~\ref{zoneslocks} you can see that there are two threads: \emph{Main thread} and \emph{Streaming thread}\footnote{By clicking on a thread name, you can temporarily disable the display of the zones in this thread.}. We can see that the \emph{Main thread} has two root level zones visible: \emph{Update} and \emph{Render}. The \emph{Update} zone is split into further sub-zones, some of which are too small to be displayed at the current zoom level. This is indicated by drawing a zig-zag pattern over the merged zones box (section~\ref{collapseditems}), with the number of collapsed zones printed in place of the zone name. We can also see that the \emph{Physics} zone acquires the \emph{Physics lock} mutex for most of its run time.
@@ -3634,17 +3697,17 @@ The numerical data values (figure~\ref{plot}) are plotted right below the zones
\label{plot}
\end{figure}
When memory profiling (section~\ref{memoryprofiling}) is enabled, Tracy will automatically generate a \emph{\faMemory{}~Memory usage} plot, which has extended capabilities. For example, hovering over a data point (memory allocation event) will visually display the allocation duration. Clicking the \LMB{} left mouse button on the data point will open the memory allocation information window, which will show the duration of the allocation as long as the window is open.
When memory profiling (section~\ref{memoryprofiling}) is enabled, Tracy will automatically generate a \emph{\faMemory{}~Memory usage} plot, which has extended capabilities. For example, hovering over a data point (memory allocation event) will visually display the allocation duration. Clicking the \LMB{}~left mouse button on the data point will open the memory allocation information window, which will show the duration of the allocation as long as the window is open.
Another plot that Tracy automatically provides is the \emph{\faTachometer*{}~CPU usage} plot, which represents the total system CPU usage percentage (it is not limited to the profiled application).
\subsubsection{Navigating the view}
Hovering the \faMousePointer{} mouse pointer over the timeline view will display a vertical line that you can use to line up events in multiple threads visually. Dragging the \LMB{} left mouse button will display the time measurement of the selected region.
Hovering the \faMousePointer{} mouse pointer over the timeline view will display a vertical line that you can use to line up events in multiple threads visually. Dragging the \LMB{}~left mouse button will display the time measurement of the selected region.
The timeline view may be scrolled both vertically and horizontally by dragging the \RMB{} right mouse button. Note that only the zones, locks, and plots scroll vertically, while the time scale and frame sets always stay on the top.
The timeline view may be scrolled both vertically and horizontally by dragging the \RMB{}~right mouse button. Note that only the zones, locks, and plots scroll vertically, while the time scale and frame sets always stay on the top.
You can zoom in and out the timeline view by using the \Scroll{}~mouse wheel. Pressing the \keys{\ctrl} key will make zooming more precise while pressing the \keys{\shift} key will make it faster. You can select a range to which you want to zoom in by dragging the \MMB{} middle mouse button. Dragging the \MMB{} middle mouse button while the \keys{\ctrl} key is pressed will zoom out.
You can zoom in and out the timeline view by using the \Scroll{}~mouse wheel. Pressing the \keys{\ctrl} key will make zooming more precise while pressing the \keys{\shift} key will make it faster. You can select a range to which you want to zoom in by dragging the \MMB{}~middle mouse button. Dragging the \MMB{}~middle mouse button while the \keys{\ctrl} key is pressed will zoom out.
It is also possible to navigate the timeline using the keyboard. The \keys{A} and \keys{D} keys scroll the view to the left and right, respectively. The \keys{W} and \keys{S} keys change the zoom level.
@@ -3742,10 +3805,15 @@ Function names in the remaining places across the UI will be normalized unless t
Disabling the display of some events is especially recommended when the profiler performance drops below acceptable levels for interactive usage.
It is possible to store defaults for the settings marked with a \emph{*} to the global Tracy configuration file.
This can be done using the \emph{Save current options as defaults} button at the bottom of the window, or by manually editing this configuration file (for which the path is indicated in the tooltip).
Next time you use Tracy, these stored default options will be used instead.
For now, restoring the defaults can be done by deleting the configuration file.
\subsection{Messages window}
\label{messages}
In this window, you can see all the messages that were sent by the client application, as described in section~\ref{messagelog}. The window is split into four columns: \emph{time}, \emph{thread}, \emph{message} and \emph{call stack}. Hovering the \faMousePointer{}~mouse cursor over a message will highlight it on the timeline view. Clicking the \LMB{} left mouse button on a message will center the timeline view on the selected message.
In this window, you can see all the messages that were sent by the client application, as described in section~\ref{messagelog}. The window is split into four columns: \emph{time}, \emph{thread}, \emph{message} and \emph{call stack}. Hovering the \faMousePointer{}~mouse cursor over a message will highlight it on the timeline view. Clicking the \LMB{}~left mouse button on a message will center the timeline view on the selected message.
The \emph{call stack} column is filled only if a call stack capture was requested, as described in section~\ref{collectingcallstacks}. A single entry consists of the \emph{\faAlignJustify{}~Show} button, which opens the call stack information window (chapter~\ref{callstackwindow}) and of abbreviated information about the call path.
@@ -3775,7 +3843,7 @@ Here you will find a multi-column display of captured zones, which contains: the
In the \emph{~Timing} menu, the \emph{~With children} selection displays inclusive measurements, that is, containing execution time of zone's children. The \emph{~Self only} selection switches the measurement to exclusive, displaying just the time spent in the zone, subtracting the child calls. Finally, the \emph{~Non-reentrant} selection shows inclusive time but counts only the first appearance of a given zone on a thread's stack.
Clicking the \LMB{} left mouse button on a zone will open the individual zone statistics view in the find zone window (section~\ref{findzone}).
Clicking the \LMB{}~left mouse button on a zone will open the individual zone statistics view in the find zone window (section~\ref{findzone}).
You can filter the displayed list of zones by matching the zone name to the expression in the \emph{\faFilter{}~Filter zones} entry field. Refer to section~\ref{messages} for a more detailed description of the expression syntax.
@@ -3833,7 +3901,7 @@ Tracy gives you the ability to display an execution time histogram of all occurr
You start by entering a search query, which will be matched against known zone names (see section~\ref{markingzones} for information on the grouping of zone names). If the search found some results, you will be presented with a list of zones in the \emph{matched source locations} drop-down. The selected zone's graph is displayed on the \emph{histogram} drop-down, and also the matching zones are highlighted on the timeline view.
Clicking the \RMB{} right mouse button on the source file location will open the source file view window (if applicable, see section~\ref{sourceview}). If symbol data is available Tracy will try to match the instrumented zone name to a captured symbol. If this succeeds and there are no duplicate matches, the source file view will be accompanied by the disassembly of the code. Since this matching is not exact, in rare cases you may get the wrong data here. To just display the source code, press and hold the \keys{\ctrl} key while clicking the \RMB{} right mouse button.
Clicking the \RMB{}~right mouse button on the source file location will open the source file view window (if applicable, see section~\ref{sourceview}). If symbol data is available Tracy will try to match the instrumented zone name to a captured symbol. If this succeeds and there are no duplicate matches, the source file view will be accompanied by the disassembly of the code. Since this matching is not exact, in rare cases you may get the wrong data here. To just display the source code, press and hold the \keys{\ctrl} key while clicking the \RMB{}~right mouse button.
An example histogram is presented in figure~\ref{findzonehistogram}. Here you can see that the majority of zone calls (by count) are clustered in the 300~\si{\nano\second} group, closely followed by the 10~\si{\micro\second} cluster. There are some outliers at the 1~and~10~\si{\milli\second} marks, which can be ignored on most occasions, as these are single occurrences.
@@ -3889,7 +3957,7 @@ Various data statistics about displayed data accompany the histogram, for exampl
\item \emph{Minimum values in bin} -- Excludes display of bins that do not hold enough values at both ends of the time range. Increasing this parameter will eliminate outliers, allowing us to concentrate on the interesting part of the graph.
\end{itemize}
You can drag the \LMB{} left mouse button over the histogram to select a time range that you want to look at closely. This will display the data in the histogram info section, and it will also filter zones shown in the \emph{found zones} section. This is quite useful if you actually want to look at the outliers, i.e.,\ where did they originate from, what the program was doing at the moment, etc\footnote{More often than not you will find out, that the application was just starting, or access to a cold file was required and there's not much you can do to optimize that particular case.}. You can reset the selection range by pressing the \RMB{} right mouse button on the histogram.
You can drag the \LMB{}~left mouse button over the histogram to select a time range that you want to look at closely. This will display the data in the histogram info section, and it will also filter zones shown in the \emph{found zones} section. This is quite useful if you actually want to look at the outliers, i.e.,\ where did they originate from, what the program was doing at the moment, etc\footnote{More often than not you will find out, that the application was just starting, or access to a cold file was required and there's not much you can do to optimize that particular case.}. You can reset the selection range by pressing the \RMB{}~right mouse button on the histogram.
The \emph{found zones} section displays the individual zones grouped according to the following criteria:
@@ -3902,9 +3970,9 @@ The \emph{found zones} section displays the individual zones grouped according t
\item \emph{No grouping} -- Disables zone grouping. It may be useful when you want to see zones in order as they appear.
\end{itemize}
You may sort each group according to the \emph{order} in which it appeared, the call \emph{count}, the total \emph{time} spent in the group, or the \emph{mean time per call}. Expanding the group view will display individual occurrences of the zone, which can be sorted by application's time, execution time, or zone's name. Clicking the \LMB{} left mouse button on a zone will open the zone information window (section~\ref{zoneinfo}). Clicking the \MMB{} middle mouse button on a zone will zoom the timeline view to the zone's extent.
You may sort each group according to the \emph{order} in which it appeared, the call \emph{count}, the total \emph{time} spent in the group, or the \emph{mean time per call}. Expanding the group view will display individual occurrences of the zone, which can be sorted by application's time, execution time, or zone's name. Clicking the \LMB{}~left mouse button on a zone will open the zone information window (section~\ref{zoneinfo}). Clicking the \MMB{}~middle mouse button on a zone will zoom the timeline view to the zone's extent.
Clicking the \LMB{} left mouse button on the group name will highlight the group time data on the histogram (figure~\ref{findzonehistogramgroup}). This function provides a quick insight into the impact of the originating thread or input data on the zone performance. Clicking on the \emph{\faBackspace~Clear} button will reset the group selection. If the grouping mode is set to \emph{Parent} option, clicking the \MMB{}~middle mouse button on the parent zone group will switch the find zone view to display the selected zone.
Clicking the \LMB{}~left mouse button on the group name will highlight the group time data on the histogram (figure~\ref{findzonehistogramgroup}). This function provides a quick insight into the impact of the originating thread or input data on the zone performance. Clicking on the \emph{\faBackspace~Clear} button will reset the group selection. If the grouping mode is set to \emph{Parent} option, clicking the \MMB{}~middle mouse button on the parent zone group will switch the find zone view to display the selected zone.
\begin{figure}[h]
\centering\begin{tikzpicture}
@@ -4119,7 +4187,7 @@ You can view the data gathered by profiling memory usage (section~\ref{memorypro
The top row contains statistics, such as \emph{total allocations} count, number of \emph{active allocations}, current \emph{memory usage} and process \emph{memory span}\footnote{Memory span describes the address space consumed by the program. It is calculated as a difference between the maximum and minimum observed in-use memory address.}.
The lists of captured memory allocations are displayed in a common multi-column format through the profiler. The first column specifies the memory address of an allocation or an address and an offset if the address is not at the start of the allocation. Clicking the \LMB{} left mouse button on an address will open the memory allocation information window\footnote{While the allocation information window is opened, the address will be highlighted on the list.} (see section~\ref{memallocinfo}). Clicking the \MMB{}~middle mouse button on an address will zoom the timeline view to memory allocation's range. The next column contains the allocation size.
The lists of captured memory allocations are displayed in a common multi-column format through the profiler. The first column specifies the memory address of an allocation or an address and an offset if the address is not at the start of the allocation. Clicking the \LMB{}~left mouse button on an address will open the memory allocation information window\footnote{While the allocation information window is opened, the address will be highlighted on the list.} (see section~\ref{memallocinfo}). Clicking the \MMB{}~middle mouse button on an address will zoom the timeline view to memory allocation's range. The next column contains the allocation size.
The allocation's timing data is contained in two columns: \emph{appeared at} and \emph{duration}. Clicking the \LMB{}~left mouse button on the first one will center the timeline view at the beginning of allocation, and likewise, clicking on the second one will center the timeline view at the end of allocation. Note that allocations that have not yet been freed will have their duration displayed in green color.
@@ -4257,6 +4325,8 @@ If the displayed call stack is a sampled call stack (chapter~\ref{sampling}), an
Clicking on the \emph{\faClipboard{}~Copy to clipboard} button will copy call stack to the clipboard.
Clicking on the \emph{\faRobot{}~Tracy Assist} button will attach the call stack to the automated assistant chat window (see section~\ref{tracyassist}). The assistant will then be able to reference the call stack to answer your questions. Alternatively, you can click on the button with the \RMB{}~right mouse button to display a list of predefined questions about the call stack for you to choose from.
\subsubsection{Reading call stacks}
\label{readingcallstacks}
@@ -4484,7 +4554,7 @@ Statistical data about all processes running on the system during the capture is
Each running program has an assigned process identifier (PID), which is displayed in the first column. The profiler will also display a list of thread identifiers (TIDs) if a program entry is expanded.
The \emph{running time} column shows how much processor time was used by a process or thread. The percentage may be over 100\%, as it is scaled to trace length, and multiple threads belonging to a single program may be executing simultaneously. The \emph{running regions} column displays how many times a given entry was in the \emph{running} state, and the \emph{CPU migrations} shows how many times an entry was moved from one CPU core to another when the system scheduler suspended an entry.
The \emph{running time} column shows how much processor time was used by a process or thread. The percentage may be over 100\%, as it is scaled to trace length, and multiple threads belonging to a single program may be executing simultaneously. The \emph{slices} column displays how many times a given entry was in the \emph{running} state, and the \emph{core jumps} shows how many times an entry was moved from one CPU core to another when the system scheduler suspended an entry.
The profiled program is highlighted using green color. Furthermore, the yellow highlight indicates threads known to the profiler (that is, which sent events due to instrumentation).
@@ -4537,10 +4607,218 @@ This window displays information about time range limits (section~\ref{timerange
Note that ranges displayed in the window have color hints that match the color of the striped regions on the timeline.
\subsection{Tracy Assist}
\label{tracyassist}
With Tracy Profiler, you can use GenAI features to get help using the profiler or analyzing the code you're profiling.
The automated assistant can search the user manual to answer your questions about the profiler. It can also read the source code when you ask about program performance or algorithms. It has the capacity for access to Wikipedia, the ability to search the web, and the capability to access web pages in response to general questions.
This feature can be completely disabled in the \emph{Global settings}, as described in section~\ref{aboutwindow}.
\begin{bclogo}[
noborder=true,
couleur=black!5,
logo=\bcattention
]{Caution}
Remember that the responses you receive from the automated assistant are the result of complex yet limited algorithms. While the answers may be convincing and in most cases reliable, you should always verify their accuracy.
\end{bclogo}
\begin{bclogo}[
noborder=true,
couleur=black!5,
logo=\bcquestion
]{How do I enter my OpenAI API key?}
You do not. Tracy is not a money funnel for Silicon Valley tech bros to get rich.
The only way to access the assistant is to run everything locally on your system. This ensures that everything you do stays private and that you won't be subject to forced changes in features or terms and conditions. You should own the tools you work with instead of renting them from someone else.
\end{bclogo}
\subsubsection{Service provider}
To get started, you will need to install an LLM\footnote{Large Language Model.} provider on your system. Any service that's compatible with the standard API should work, but some may work better than others. The LLM field is advancing quickly, with new models frequently being released that often require specific support from provider services to deliver the best experience.
The ideal LLM provider should be a system service that loads and unloads models on demand and swaps between them as needed. It should provide a service to a variety of user-facing applications running on the system. The ideal provider should also implement a time-to-live mechanism that unloads models after a period of inactivity to make resources available to other programs. The user should be able to use the ideal provider to find and download models that they can run on their hardware.
There are no ideal LLM providers, but here are some options:
\begin{itemize}
\item \emph{LM Studio} (\url{https://lmstudio.ai/}) -- It is the easiest to use and install on all platforms. It may be a bit overwhelming at first due to the number of options it offers. Some people may question the licensing. Its features lag behind. Manual configuration of each model is required.
\item \emph{llama.cpp} (\url{https://github.com/ggml-org/llama.cpp}) -- Recommended for advanced users. It is rapidly advancing with new features and model support. Most other providers use it to do the actual work, and they typically use an outdated release. It requires a lot of manual setup and command line usage. It does not hold your hand.
\item \emph{llama-swap} (\url{https://github.com/mostlygeek/llama-swap}) -- Wrapper for llama.cpp that allows model selection. Recommended to augment the above.
\item \emph{Ollama} (\url{https://ollama.com/}) -- It lacks some features required by Tracy. Very limited configuration is only available via the system service's environment variables. Some practices are questionable. It will not use full capabilities of the available hardware. Not recommended.
\end{itemize}
\begin{bclogo}[
noborder=true,
couleur=black!5,
logo=\bclampe
]{Example llama-swap configuration file}
Here's an example configuration for llama-swap that will provide two swappable chat models, and an vector embeddings model that will not be unloaded:
\begin{lstlisting}
macros:
"llama": >
/usr/bin/llama-server
--port ${PORT}
--flash-attn
-ngl 999
models:
"gemma3:12b":
cmd: |
${llama}
--model /home/user/models/gemma-3-12B-it-QAT-Q4_0.gguf
--ctx-size 65536
ttl: 300
"qwen3:14b":
cmd: |
${llama}
--model /home/user/models/Qwen3-14B-Q4_K_M.gguf
--ctx-size 32768
--cache-type-k q8_0
--cache-type-v q8_0
ttl: 300
"embed-nomic-text-v1.5":
cmd: |
${llama}
--model /home/user/models/nomic-embed-text-v1.5.Q8_0.gguf
-c 8192
-b 8192
-ub 4096
-np 2
--embeddings
ttl: 300
groups:
embeddings:
swap: false
exclusive: false
members:
- embed-nomic-text-v1.5
\end{lstlisting}
\end{bclogo}
\subsubsection{Model selection}
Once you have installed the service provider, you will need to download the model files for the chat functionality. The exact process depends on the provider you chose. LM Studio, for example, has a built-in downloader with an easy-to-use UI. For llama.cpp, you can follow their documentation or download the model file via your web browser.
Tracy will not issue commands to download any model on its own.
\paragraph{Model family}
There are many factors to take into consideration when choosing a model to use. First, you should determine which model family you want to use:
\begin{itemize}
\item \emph{Gemma 3} (\url{https://blog.google/technology/developers/gemma-3/}) is a well rounded model that can converse in multiple languages.
\item \emph{Qwen3} (\url{https://qwenlm.github.io/blog/qwen3/}) has a more technical feeling to it, it likes to write bullet point lists.
\item \emph{Mistral Small} (\url{https://mistral.ai/news/mistral-small-3-1}) may also be considered. Despite the name, it is not small.
\end{itemize}
This list is not exhaustive; it's only a starting point. These base models are often briefly fine-tuned to perform better at a specific task while retaining the model's general characteristics, hence the term \emph{model family}. It is recommended that you start with a base model and only explore the fine-tuned models later, if at all.
When looking for a model you may encounter models that are "reasoning". These are generally not worth the additional time and resources they need.
\paragraph{Model size}
The next thing to consider when selecting a model is its size, which is typically measured in billions of parameters (weights) and written as 4B, for example. A model's size determines how much memory, computation, and time are required to run it. Generally, the larger the model, the "smarter" its responses will be.
Models with 4B parameters are too "dumb" to operate in Tracy and will produce nonsense results. The 8B models are barely capable, so their use is not recommended. Models such as Gemma 3 12B and Qwen3 14B should work reasonably well. However, if your hardware can handle it, you should look for even larger models.
Then there are models that are "Mixture of Experts". For instance, a model may have 30B total parameters, but only 3B are active when generating a response. While these models can generate responses faster, they still require the full set of parameters to be loaded into memory. Their results are also inferior to those of "dense" models of a similar size that use all their parameters.
\paragraph{Model quantization}
Running a model with full 32-bit floating-point weights is not feasible due to memory requirements. Instead, the model parameters are quantized, for which 4 bits is typically the sweet spot. In general, the lower the parameter precision, the more "dumbed down" the model becomes. However, the loss of model coherence due to quantization is less than the benefit of being able to run a larger model.
There are different ways of doing quantization that give the same bit size. It's best to follow the recommendations provided by LM Studio, for example.
Some models consider quantization during training, resulting in a more effective model. Gemma 3 refers to this as QAT (Quantization-Aware Training).
\paragraph{Multimodality}
Some models can recognize vision or audio. This is achieved by loading an additional model alongside the language model, which increases memory requirements. Since Tracy does not require these capabilities, it's best to either avoid multimodal models or configure the LLM provider appropriately.
\paragraph{Context size}
The model size only indicates the minimum memory requirement. For the model to operate properly, you also need to set the context size, which determines how much information from the conversation the model can "remember". This size is measured in tokens, and a very rough approximation is that each token is a combination of three or four letters.
Each token present in the context window requires a fairly large amount of memory, and that quickly adds up to gigabytes. The KV cache used for context can be quantized, just like model parameters. In this case, the recommended size per weight is 8 bits.
The minimum required context size for Tracy to run the assistant is 8K, but don't expect things to run smoothly. Using 16K provides more room to operate, but it's still tight. If you have the resources, it's recommended to use 32K or even 64K.
\paragraph{Hardware resources}
Ideally, you want to keep both the model and the context cache in your GPU's VRAM. This will provide the fastest possible speed. However, this won't be possible in many configurations.
LLM providers solve this problem by storing part of the model on the GPU and running the rest on the CPU. The more that can be run on the GPU, the faster it goes.
Determining how much of the model can be run on the GPU usually requires some experimentation. Other programs running on the system may affect or be affected by this setting. Generally, GPU offload capability is measured by the number of neural network layers.
\paragraph{In practice}
So, which model should you run and what hardware you need to be able to do so? Let's take look at some example systems.
\begin{itemize}
\item On a Dell XPS 13" laptop with an i7-1185G7 CPU and integrated GPU, you will struggle to run even the most basic 4B model. Forget about it.
\item With 16 GB of RAM and a weak 4 GB Nvidia GPU, you can run Gemma 3 12B (8K context, 8/48 layers offloaded) or Qwen3 14B (16K context, 11/40 layers offloaded) on a Ryzen laptop. A moderate amount of patience will be necessary.
\item An 8 GB Nvidia GPU can reach usable speeds when running Gemma 3 12B (16K context, 28/48 layers offloaded) or Qwen3 14B (16K context, 30/40 layers offloaded).
\item If you have a 4090 class GPU with 24 GB of VRAM, llama.cpp can run Gemma 3 27B with a 64K context.
\end{itemize}
\subsubsection{Embeddings model}
To access the full functionality of the automated assistant, you will also need a second language model. While the previous section focused on the model used for conversation, we also need a model that enables searching the user manual.
This kind of model performs \emph{vector embeddings}, which transform text content or a search query into a set of concepts that match the text's meaning. These semantic vectors can then be compared to each other without needing to precisely match keywords. For instance, if a user searches for efficient text search methods, the results will include text about vector embedding models.
Embedding models can be downloaded just like conversation models. The text-nomic-embed v1.5 model is recommended, as it is known to work well. Using other models may result in catastrophic degradation of search results.\footnote{There are many reasons why:
\begin{enumerate}
\item Some models just won't work as advertised. For example, the BGE-M3 model doesn't work at all with the Tracy user manual.
\item Embedding models usually require a prefix that describes the task at hand.
\item It is better to support one model that is known to work as intended than to support many models that work poorly.
\end{enumerate}
}
LM Studio and Ollama properly label the model's capabilities. This is not the case with the llama.cpp/llama-swap setup. To make it work, your embedding model's name must contain the word \texttt{embed}.
\subsubsection{Usage}
The automated assistant can be accessed via the various \emph{\faRobot{}~Tracy Assist} buttons in the UI. The button in the control menu (section~\ref{controlmenu}) gives quick access to the chat. Buttons in other profiler windows open the chat window and add context related to the program you are profiling.
The chat window is divided into three sections:
\begin{enumerate}
\item The control section at the top.
\item The chat contents take up most of the window.
\item The entry box is at the bottom.
\end{enumerate}
The control section allows you to clear the chat contents, reconnect to the LLM provider and open the settings panel consisting of:
\begin{itemize}
\item \emph{API} -- Enter the endpoint URL of the LLM provider here. A drop-down list is provided as a convenient way to select the default configuration of various providers. Note that the drop-down list is only used to fill in the endpoint URL. While Tracy does adapt to different ways each provider behaves, the feature detection is performed based on the endpoint conversation, not the drop-down selection.
\item \emph{Model} -- Here you can select one of the models you have configured in the LLM provider for chat.
\item \emph{Embeddings} -- Select the vector embeddings model.
\item \emph{Temperature} -- Allows changing default model temperature setting.
\item \emph{Internet access} -- Determines whether the model can access network resources such as Wikipedia queries, web searches, and web page retrievals.
\item \emph{External services} -- Allows optional configuration of network access.
\begin{itemize}
\item \emph{User agent} -- Allows changing the user agent parameter in web queries.
\item \emph{Google Search Engine} and \emph{API Key} -- Enables use of Google search. If this is not set, searches will fall back to DuckDuckGo, which is very rate limited.
\end{itemize}
\end{itemize}
The \emph{\faBook{}~Learn manual} button is used to build the search index for the user manual. This process only takes a short amount of time, and the results are cached until either the embeddings model changes or the manual is updated.
The horizontal meter directly below shows how much of the context size has been used. Tracy uses various techniques to manage context size, such as limiting the amount of data provided to the model or removing older data. However, the context will eventually be fully utilized during an extended conversation, resulting in a significant degradation of the quality of model responses.
The chat section contains the conversation with the automated assistant. Each assistant reply includes a hidden "thinking" section in which various tool calls are made and the response is prepared.
Clicking on the~\emph{\faUser{}~User} role icon removes the chat content up to the selected question. Similarly, clicking on the~\emph{\faRobot{}~Assistant} role icon removes the conversation content up to this point and generates another response from the assistant.
\section{Exporting zone statistics to CSV}
\label{csvexport}
You can use a command-line utility in the \texttt{csvexport} directory to export primary zone statistics from a saved trace into a CSV format.
You can use the command-line utility \texttt{tracy-csvexport} from the \texttt{csvexport} directory to export primary zone statistics from a saved trace into a CSV format.
The tool requires a single .tracy file as an argument and prints the result into the standard output (stdout), from where you can redirect it into a file or use it as an input into another tool.
By default, the utility will list all zones with the following columns:
@@ -4574,23 +4852,23 @@ You can customize the output with the following command line options:
Tracy can import data generated by other profilers. This external data cannot be directly loaded but must be converted first.
Currently, there's support for the following formats:
\begin{itemize}
\item chrome:tracing data through the \texttt{import-chrome} utility. The trace files
\item chrome:tracing data through the \texttt{tracy-import-chrome} utility. The trace files
typically have a \texttt{.json} or \texttt{.json.zst} extension.
To use this tool to process a file named \texttt{mytracefile.json}, assuming it's compiled, run:
\begin{lstlisting}[language=sh]
$ import-chrome mytracefile.json mytracefile.tracy
$ tracy mytracefile.tracy
$ tracy-import-chrome mytracefile.json mytracefile.tracy
$ tracy-profiler mytracefile.tracy
\end{lstlisting}
\item Fuchsia's tracing format\footnote{\url{https://fuchsia.dev/fuchsia-src/reference/tracing/trace-format}}
data through the \texttt{import-fuchsia} utility.
data through the \texttt{tracy-import-fuchsia} utility.
This format has many commonalities with the chrome:tracing format, but it uses a
compact and efficient binary encoding that can help lower tracing overhead.
The file extension is \texttt{.fxt} or \texttt{.fxt.zst}.
To this this tool, assuming it's compiled, run:
\begin{lstlisting}[language=sh]
$ import-fuchsia mytracefile.fxt mytracefile.tracy
$ tracy mytracefile.tracy
$ tracy-import-fuchsia mytracefile.fxt mytracefile.tracy
$ tracy-profiler mytracefile.tracy
\end{lstlisting}
\end{itemize}
@@ -4608,8 +4886,8 @@ noborder=true,
couleur=black!5,
logo=\bclampe
]{Source locations}
Chrome tracing format doesn't document a way to provide source location data.
The \texttt{import-chrome} and \texttt{import-fuchsia} utilities will however recognize a custom \texttt{loc} tag in the root of zone begin events. You should be formatting this data in the usual \texttt{filename:line} style, for example: \texttt{hello.c:42}. Providing the line number (including a colon) is optional but highly recommended.
Chrome tracing format doesn't provide a well-defined way to provide source location data.
The \texttt{tracy-import-chrome} and \texttt{tracy-import-fuchsia} utilities will however recognize a custom \texttt{loc} tag in the root of zone begin events. You should be formatting this data in the usual \texttt{filename:line} style, for example: \texttt{hello.c:42}. Providing the line number (including a colon) is optional but highly recommended.
\end{bclogo}
\begin{bclogo}[
@@ -4643,6 +4921,12 @@ This external data is stored in the \texttt{user/[letter]/[program]/[week]/[epoc
The profiler never prunes user settings.
\subsection{Cache files}
Some of the profiler's features may want to store cache files on your disk. You can always get rid of these data files because they're only used to speed up some long operations that may precalculate data once and then reuse it.
On Windows cache is stored in the \texttt{\%LOCALAPPDATA\%/tracy} directory. All other platforms use the \texttt{\$XDG\_CACHE\_HOME/tracy} directory, or \texttt{\$HOME/.cache/tracy} if the \texttt{XDG\_CACHE\_HOME} environment variable is not set.
\newpage
\appendix
\appendixpage

View File

@@ -177,7 +177,7 @@ common_includes = [
'public/common/TracySocket.hpp',
'public/common/TracyStackFrames.hpp',
'public/common/TracySystem.hpp',
'public/common/TracyUwp.hpp',
'public/common/TracyWinFamily.hpp',
'public/common/TracyYield.hpp'
]

View File

@@ -1,233 +0,0 @@
cmake_minimum_required(VERSION 3.21)
option(NO_FILESELECTOR "Disable the file selector" OFF)
option(GTK_FILESELECTOR "Use the GTK file selector on Linux instead of the xdg-portal one" OFF)
option(LEGACY "Instead of Wayland, use the legacy X11 backend on Linux" OFF)
option(NO_ISA_EXTENSIONS "Disable ISA extensions (don't pass -march=native or -mcpu=native to the compiler)" OFF)
option(NO_STATISTICS "Disable calculation of statistics" OFF)
option(SELF_PROFILE "Enable self-profiling" OFF)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/version.cmake)
set(CMAKE_CXX_STANDARD 20)
project(
tracy-profiler
LANGUAGES C CXX
VERSION ${TRACY_VERSION_STRING}
)
if(SELF_PROFILE)
add_definitions(-DTRACY_ENABLE)
add_compile_options(-g -O3 -fno-omit-frame-pointer)
endif()
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/config.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/vendor.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/server.cmake)
set(SERVER_FILES
TracyAchievementData.cpp
TracyAchievements.cpp
TracyBadVersion.cpp
TracyColor.cpp
TracyEventDebug.cpp
TracyFileselector.cpp
TracyFilesystem.cpp
TracyImGui.cpp
TracyMicroArchitecture.cpp
TracyMouse.cpp
TracyProtoHistory.cpp
TracySourceContents.cpp
TracySourceTokenizer.cpp
TracySourceView.cpp
TracyStorage.cpp
TracyTexture.cpp
TracyTimelineController.cpp
TracyTimelineItem.cpp
TracyTimelineItemCpuData.cpp
TracyTimelineItemGpu.cpp
TracyTimelineItemPlot.cpp
TracyTimelineItemThread.cpp
TracyUserData.cpp
TracyUtility.cpp
TracyView.cpp
TracyView_Annotations.cpp
TracyView_Callstack.cpp
TracyView_Compare.cpp
TracyView_ConnectionState.cpp
TracyView_ContextSwitch.cpp
TracyView_CpuData.cpp
TracyView_FindZone.cpp
TracyView_FlameGraph.cpp
TracyView_FrameOverview.cpp
TracyView_FrameTimeline.cpp
TracyView_FrameTree.cpp
TracyView_GpuTimeline.cpp
TracyView_Locks.cpp
TracyView_Memory.cpp
TracyView_Messages.cpp
TracyView_Navigation.cpp
TracyView_NotificationArea.cpp
TracyView_Options.cpp
TracyView_Playback.cpp
TracyView_Plots.cpp
TracyView_Ranges.cpp
TracyView_Samples.cpp
TracyView_Statistics.cpp
TracyView_Timeline.cpp
TracyView_TraceInfo.cpp
TracyView_Utility.cpp
TracyView_ZoneInfo.cpp
TracyView_ZoneTimeline.cpp
TracyWeb.cpp
)
list(TRANSFORM SERVER_FILES PREPEND "src/profiler/")
set(PROFILER_FILES
src/ConnectionHistory.cpp
src/Filters.cpp
src/Fonts.cpp
src/HttpRequest.cpp
src/ImGuiContext.cpp
src/ini.c
src/IsElevated.cpp
src/main.cpp
src/ResolvService.cpp
src/RunQueue.cpp
src/WindowPosition.cpp
src/winmain.cpp
src/winmainArchDiscovery.cpp
)
set(INCLUDES "${CMAKE_CURRENT_BINARY_DIR}")
set(LIBS "")
if(USE_WAYLAND)
pkg_check_modules(WAYLAND REQUIRED egl wayland-egl wayland-cursor xkbcommon)
set(INCLUDES "${INCLUDES};${WAYLAND_INCLUDE_DIRS}")
set(LIBS "${LIBS};${WAYLAND_LIBRARIES}")
set(PROFILER_FILES ${PROFILER_FILES}
src/BackendWayland.cpp
)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/FindWaylandScanner.cmake)
CPMAddPackage(
NAME wayland-protocols
GIT_REPOSITORY https://gitlab.freedesktop.org/wayland/wayland-protocols.git
GIT_TAG 1.37
DOWNLOAD_ONLY YES
)
ecm_add_wayland_client_protocol(PROFILER_FILES
PROTOCOL ${wayland-protocols_SOURCE_DIR}/stable/xdg-shell/xdg-shell.xml
BASENAME xdg-shell
)
ecm_add_wayland_client_protocol(PROFILER_FILES
PROTOCOL ${wayland-protocols_SOURCE_DIR}/staging/xdg-activation/xdg-activation-v1.xml
BASENAME xdg-activation
)
ecm_add_wayland_client_protocol(PROFILER_FILES
PROTOCOL ${wayland-protocols_SOURCE_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml
BASENAME xdg-decoration
)
ecm_add_wayland_client_protocol(PROFILER_FILES
PROTOCOL ${wayland-protocols_SOURCE_DIR}/staging/fractional-scale/fractional-scale-v1.xml
BASENAME fractional-scale
)
ecm_add_wayland_client_protocol(PROFILER_FILES
PROTOCOL ${wayland-protocols_SOURCE_DIR}/stable/viewporter/viewporter.xml
BASENAME viewporter
)
ecm_add_wayland_client_protocol(PROFILER_FILES
PROTOCOL ${wayland-protocols_SOURCE_DIR}/staging/cursor-shape/cursor-shape-v1.xml
BASENAME cursor-shape
)
ecm_add_wayland_client_protocol(PROFILER_FILES
PROTOCOL ${wayland-protocols_SOURCE_DIR}/unstable/tablet/tablet-unstable-v2.xml
BASENAME tablet
)
ecm_add_wayland_client_protocol(PROFILER_FILES
PROTOCOL ${wayland-protocols_SOURCE_DIR}/staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml
BASENAME xdg-toplevel-icon
)
elseif(EMSCRIPTEN)
set(PROFILER_FILES ${PROFILER_FILES}
src/BackendEmscripten.cpp
)
else()
set(PROFILER_FILES ${PROFILER_FILES}
src/BackendGlfw.cpp
${ImGui_SOURCE_DIR}/backends/imgui_impl_glfw.cpp
)
endif()
include_directories(${INCLUDES})
link_libraries(${LIBS})
if(SELF_PROFILE)
set(PROFILER_FILES ${PROFILER_FILES}
../public/TracyClient.cpp
)
endif()
if(WIN32)
set(PROFILER_FILES ${PROFILER_FILES}
win32/Tracy.manifest
win32/Tracy.rc
)
add_executable(${PROJECT_NAME} WIN32 ${PROFILER_FILES} ${COMMON_FILES} ${SERVER_FILES})
set_property(DIRECTORY ${CMAKE_CURRENT_LIST_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
else()
add_executable(${PROJECT_NAME} ${PROFILER_FILES} ${COMMON_FILES} ${SERVER_FILES})
endif()
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE TracyServer TracyImGui Threads::Threads)
if(NOT DEFINED GIT_REV)
set(GIT_REV "HEAD")
endif()
find_package(Git)
if(Git_FOUND)
add_custom_target(git-ref
COMMAND ${CMAKE_COMMAND} -E echo "#pragma once" > GitRef.hpp.tmp
COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} log -1 "--format=namespace tracy { static inline const char* GitRef = %x22%h%x22; }" ${GIT_REV} >> GitRef.hpp.tmp || echo "namespace tracy { static inline const char* GitRef = \"unknown\"; }" >> GitRef.hpp.tmp
COMMAND ${CMAKE_COMMAND} -E copy_if_different GitRef.hpp.tmp GitRef.hpp
BYPRODUCTS GitRef.hpp GitRef.hpp.tmp
VERBATIM
)
add_dependencies(${PROJECT_NAME} git-ref)
else()
message(WARNING "git not found, using 'unknown' as git ref.")
add_custom_command(
OUTPUT GitRef.hpp
COMMAND ${CMAKE_COMMAND} -E echo "#pragma once" > GitRef.hpp
COMMAND ${CMAKE_COMMAND} -E echo "namespace tracy { static inline const char* GitRef = \"unknown\"; }" >> GitRef.hpp
VERBATIM
)
target_sources(${PROJECT_NAME} PUBLIC GitRef.hpp)
endif()
if(NOT EMSCRIPTEN)
if(NOT NO_FILESELECTOR)
target_link_libraries(${PROJECT_NAME} PRIVATE nfd::nfd)
endif()
if(NOT USE_WAYLAND)
target_link_libraries(${PROJECT_NAME} PRIVATE TracyGlfw3)
endif()
endif()
if(EMSCRIPTEN)
target_link_options(${PROJECT_NAME} PRIVATE -pthread -sASSERTIONS=0 -sINITIAL_MEMORY=384mb -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=4gb -sSTACK_SIZE=1048576 -sWASM_BIGINT=1 -sPTHREAD_POOL_SIZE=8 -sEXPORTED_FUNCTIONS=_main,_nativeOpenFile -sEXPORTED_RUNTIME_METHODS=ccall -sENVIRONMENT=web,worker --preload-file embed.tracy)
file(DOWNLOAD https://share.nereid.pl/i/embed.tracy ${CMAKE_CURRENT_BINARY_DIR}/embed.tracy EXPECTED_MD5 ca0fa4f01e7b8ca5581daa16b16c768d)
file(COPY ${CMAKE_CURRENT_LIST_DIR}/wasm/index.html DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(COPY ${CMAKE_CURRENT_LIST_DIR}/wasm/httpd.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(COPY_FILE ${CMAKE_CURRENT_LIST_DIR}/../icon/icon.svg ${CMAKE_CURRENT_BINARY_DIR}/favicon.svg)
endif()
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@@ -1,34 +0,0 @@
#ifndef __BACKEND_HPP__
#define __BACKEND_HPP__
#include <functional>
#include <stdint.h>
#include "WindowPosition.hpp"
class RunQueue;
class Backend
{
public:
Backend( const char* title, const std::function<void()>& redraw, const std::function<void(float)>& scaleChanged, const std::function<int(void)>& isBusy, RunQueue* mainThreadTasks );
~Backend();
void Show();
void Run();
void Attention();
void NewFrame( int& w, int& h );
void EndFrame();
void SetIcon( uint8_t* data, int w, int h );
void SetTitle( const char* title );
float GetDpiScale();
private:
WindowPosition m_winPos;
int m_w, m_h;
};
#endif

View File

@@ -1,341 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <emscripten/html5.h>
#include <backends/imgui_impl_opengl3.h>
#include "Backend.hpp"
#include "RunQueue.hpp"
#include "profiler/TracyImGui.hpp"
static std::function<void()> s_redraw;
static std::function<void(float)> s_scaleChanged;
static std::function<int(void)> s_isBusy;
static RunQueue* s_mainThreadTasks;
static EGLDisplay s_eglDpy;
static EGLContext s_eglCtx;
static EGLSurface s_eglSurf;
static float s_prevScale = -1;
static int s_width, s_height;
static uint64_t s_time;
static const char* s_prevCursor = nullptr;
static ImGuiKey TranslateKeyCode( const char* code )
{
if( strcmp( code, "Backquote" ) == 0 ) return ImGuiKey_GraveAccent;
if( strcmp( code, "Backslash" ) == 0 ) return ImGuiKey_Backslash;
if( strcmp( code, "BracketLeft" ) == 0 ) return ImGuiKey_LeftBracket;
if( strcmp( code, "BracketRight" ) == 0 ) return ImGuiKey_RightBracket;
if( strcmp( code, "Comma" ) == 0 ) return ImGuiKey_Comma;
if( strcmp( code, "Digit0" ) == 0 ) return ImGuiKey_0;
if( strcmp( code, "Digit1" ) == 0 ) return ImGuiKey_1;
if( strcmp( code, "Digit2" ) == 0 ) return ImGuiKey_2;
if( strcmp( code, "Digit3" ) == 0 ) return ImGuiKey_3;
if( strcmp( code, "Digit4" ) == 0 ) return ImGuiKey_4;
if( strcmp( code, "Digit5" ) == 0 ) return ImGuiKey_5;
if( strcmp( code, "Digit6" ) == 0 ) return ImGuiKey_6;
if( strcmp( code, "Digit7" ) == 0 ) return ImGuiKey_7;
if( strcmp( code, "Digit8" ) == 0 ) return ImGuiKey_8;
if( strcmp( code, "Digit9" ) == 0 ) return ImGuiKey_9;
if( strcmp( code, "Equal" ) == 0 ) return ImGuiKey_Equal;
if( strcmp( code, "IntlBackslash" ) == 0 ) return ImGuiKey_Backslash;
if( strcmp( code, "IntlRo" ) == 0 ) return ImGuiKey_Backslash;
if( strcmp( code, "IntlYen" ) == 0 ) return ImGuiKey_Backslash;
if( strcmp( code, "KeyA" ) == 0 ) return ImGuiKey_A;
if( strcmp( code, "KeyB" ) == 0 ) return ImGuiKey_B;
if( strcmp( code, "KeyC" ) == 0 ) return ImGuiKey_C;
if( strcmp( code, "KeyD" ) == 0 ) return ImGuiKey_D;
if( strcmp( code, "KeyE" ) == 0 ) return ImGuiKey_E;
if( strcmp( code, "KeyF" ) == 0 ) return ImGuiKey_F;
if( strcmp( code, "KeyG" ) == 0 ) return ImGuiKey_G;
if( strcmp( code, "KeyH" ) == 0 ) return ImGuiKey_H;
if( strcmp( code, "KeyI" ) == 0 ) return ImGuiKey_I;
if( strcmp( code, "KeyJ" ) == 0 ) return ImGuiKey_J;
if( strcmp( code, "KeyK" ) == 0 ) return ImGuiKey_K;
if( strcmp( code, "KeyL" ) == 0 ) return ImGuiKey_L;
if( strcmp( code, "KeyM" ) == 0 ) return ImGuiKey_M;
if( strcmp( code, "KeyN" ) == 0 ) return ImGuiKey_N;
if( strcmp( code, "KeyO" ) == 0 ) return ImGuiKey_O;
if( strcmp( code, "KeyP" ) == 0 ) return ImGuiKey_P;
if( strcmp( code, "KeyQ" ) == 0 ) return ImGuiKey_Q;
if( strcmp( code, "KeyR" ) == 0 ) return ImGuiKey_R;
if( strcmp( code, "KeyS" ) == 0 ) return ImGuiKey_S;
if( strcmp( code, "KeyT" ) == 0 ) return ImGuiKey_T;
if( strcmp( code, "KeyU" ) == 0 ) return ImGuiKey_U;
if( strcmp( code, "KeyV" ) == 0 ) return ImGuiKey_V;
if( strcmp( code, "KeyW" ) == 0 ) return ImGuiKey_W;
if( strcmp( code, "KeyX" ) == 0 ) return ImGuiKey_X;
if( strcmp( code, "KeyY" ) == 0 ) return ImGuiKey_Y;
if( strcmp( code, "KeyZ" ) == 0 ) return ImGuiKey_Z;
if( strcmp( code, "Minus" ) == 0 ) return ImGuiKey_Minus;
if( strcmp( code, "Period" ) == 0 ) return ImGuiKey_Period;
if( strcmp( code, "Quote" ) == 0 ) return ImGuiKey_Apostrophe;
if( strcmp( code, "Semicolon" ) == 0 ) return ImGuiKey_Semicolon;
if( strcmp( code, "Slash" ) == 0 ) return ImGuiKey_Slash;
if( strcmp( code, "AltLeft" ) == 0 ) return ImGuiKey_LeftAlt;
if( strcmp( code, "AltRight" ) == 0 ) return ImGuiKey_RightAlt;
if( strcmp( code, "Backspace" ) == 0 ) return ImGuiKey_Backspace;
if( strcmp( code, "CapsLock" ) == 0 ) return ImGuiKey_CapsLock;
if( strcmp( code, "ContextMenu" ) == 0 ) return ImGuiKey_Menu;
if( strcmp( code, "ControlLeft" ) == 0 ) return ImGuiKey_LeftCtrl;
if( strcmp( code, "ControlRight" ) == 0 ) return ImGuiKey_RightCtrl;
if( strcmp( code, "Enter" ) == 0 ) return ImGuiKey_Enter;
if( strcmp( code, "MetaLeft" ) == 0 ) return ImGuiKey_LeftSuper;
if( strcmp( code, "MetaRight" ) == 0 ) return ImGuiKey_RightSuper;
if( strcmp( code, "ShiftLeft" ) == 0 ) return ImGuiKey_LeftShift;
if( strcmp( code, "ShiftRight" ) == 0 ) return ImGuiKey_RightShift;
if( strcmp( code, "Space" ) == 0 ) return ImGuiKey_Space;
if( strcmp( code, "Tab" ) == 0 ) return ImGuiKey_Tab;
if( strcmp( code, "Delete" ) == 0 ) return ImGuiKey_Delete;
if( strcmp( code, "End" ) == 0 ) return ImGuiKey_End;
if( strcmp( code, "Home" ) == 0 ) return ImGuiKey_Home;
if( strcmp( code, "Insert" ) == 0 ) return ImGuiKey_Insert;
if( strcmp( code, "PageDown" ) == 0 ) return ImGuiKey_PageDown;
if( strcmp( code, "PageUp" ) == 0 ) return ImGuiKey_PageUp;
if( strcmp( code, "ArrowDown" ) == 0 ) return ImGuiKey_DownArrow;
if( strcmp( code, "ArrowLeft" ) == 0 ) return ImGuiKey_LeftArrow;
if( strcmp( code, "ArrowRight" ) == 0 ) return ImGuiKey_RightArrow;
if( strcmp( code, "ArrowUp" ) == 0 ) return ImGuiKey_UpArrow;
if( strcmp( code, "NumLock" ) == 0 ) return ImGuiKey_NumLock;
if( strcmp( code, "Numpad0" ) == 0 ) return ImGuiKey_Keypad0;
if( strcmp( code, "Numpad1" ) == 0 ) return ImGuiKey_Keypad1;
if( strcmp( code, "Numpad2" ) == 0 ) return ImGuiKey_Keypad2;
if( strcmp( code, "Numpad3" ) == 0 ) return ImGuiKey_Keypad3;
if( strcmp( code, "Numpad4" ) == 0 ) return ImGuiKey_Keypad4;
if( strcmp( code, "Numpad5" ) == 0 ) return ImGuiKey_Keypad5;
if( strcmp( code, "Numpad6" ) == 0 ) return ImGuiKey_Keypad6;
if( strcmp( code, "Numpad7" ) == 0 ) return ImGuiKey_Keypad7;
if( strcmp( code, "Numpad8" ) == 0 ) return ImGuiKey_Keypad8;
if( strcmp( code, "Numpad9" ) == 0 ) return ImGuiKey_Keypad9;
if( strcmp( code, "NumpadAdd" ) == 0 ) return ImGuiKey_KeypadAdd;
if( strcmp( code, "NumpadBackspace" ) == 0 ) return ImGuiKey_Backspace;
if( strcmp( code, "NumpadComma" ) == 0 ) return ImGuiKey_KeypadDecimal;
if( strcmp( code, "NumpadDecimal" ) == 0 ) return ImGuiKey_KeypadDecimal;
if( strcmp( code, "NumpadDivide" ) == 0 ) return ImGuiKey_KeypadDivide;
if( strcmp( code, "NumpadEnter" ) == 0 ) return ImGuiKey_KeypadEnter;
if( strcmp( code, "NumpadEqual" ) == 0 ) return ImGuiKey_KeypadEqual;
if( strcmp( code, "NumpadMultiply" ) == 0 ) return ImGuiKey_KeypadMultiply;
if( strcmp( code, "NumpadSubtract" ) == 0 ) return ImGuiKey_KeypadSubtract;
if( strcmp( code, "Escape" ) == 0 ) return ImGuiKey_Escape;
if( strcmp( code, "F1" ) == 0 ) return ImGuiKey_F1;
if( strcmp( code, "F2" ) == 0 ) return ImGuiKey_F2;
if( strcmp( code, "F3" ) == 0 ) return ImGuiKey_F3;
if( strcmp( code, "F4" ) == 0 ) return ImGuiKey_F4;
if( strcmp( code, "F5" ) == 0 ) return ImGuiKey_F5;
if( strcmp( code, "F6" ) == 0 ) return ImGuiKey_F6;
if( strcmp( code, "F7" ) == 0 ) return ImGuiKey_F7;
if( strcmp( code, "F8" ) == 0 ) return ImGuiKey_F8;
if( strcmp( code, "F9" ) == 0 ) return ImGuiKey_F9;
if( strcmp( code, "F10" ) == 0 ) return ImGuiKey_F10;
// F11 is browser fullscreen, F12 is browser dev tools, omitting them
if( strcmp( code, "ScrollLock" ) == 0 ) return ImGuiKey_ScrollLock;
if( strcmp( code, "Pause" ) == 0 ) return ImGuiKey_Pause;
return ImGuiKey_None;
}
Backend::Backend( const char* title, const std::function<void()>& redraw, const std::function<void(float)>& scaleChanged, const std::function<int(void)>& isBusy, RunQueue* mainThreadTasks )
{
constexpr EGLint eglConfigAttrib[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_NONE
};
s_eglDpy = eglGetDisplay( EGL_DEFAULT_DISPLAY );
EGLBoolean res;
res = eglInitialize( s_eglDpy, nullptr, nullptr );
if( res != EGL_TRUE ) { fprintf( stderr, "Cannot initialize EGL!\n" ); exit( 1 ); }
EGLint count;
EGLConfig eglConfig;
res = eglChooseConfig( s_eglDpy, eglConfigAttrib, &eglConfig, 1, &count );
if( res != EGL_TRUE || count != 1 ) { fprintf( stderr, "No suitable EGL config found!\n" ); exit( 1 ); }
s_eglSurf = eglCreateWindowSurface( s_eglDpy, eglConfig, 0, nullptr );
constexpr EGLint eglCtxAttrib[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
s_eglCtx = eglCreateContext( s_eglDpy, eglConfig, EGL_NO_CONTEXT, eglCtxAttrib );
if( !s_eglCtx ) { fprintf( stderr, "Cannot create OpenGL 3.2 Core Profile context!\n" ); exit( 1 ); }
res = eglMakeCurrent( s_eglDpy, s_eglSurf, s_eglSurf, s_eglCtx );
if( res != EGL_TRUE ) { fprintf( stderr, "Cannot make EGL context current!\n" ); exit( 1 ); }
ImGui_ImplOpenGL3_Init( "#version 100" );
EM_ASM( document.title = UTF8ToString($0), title );
s_redraw = redraw;
s_scaleChanged = scaleChanged;
s_isBusy = isBusy;
s_mainThreadTasks = mainThreadTasks;
ImGuiIO& io = ImGui::GetIO();
io.BackendPlatformName = "wasm (tracy profiler)";
emscripten_set_mousedown_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL {
ImGui::GetIO().AddMouseButtonEvent( e->button == 0 ? 0 : 3 - e->button, true );
tracy::s_wasActive = true;
return EM_TRUE;
} );
emscripten_set_mouseup_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL {
ImGui::GetIO().AddMouseButtonEvent( e->button == 0 ? 0 : 3 - e->button, false );
tracy::s_wasActive = true;
return EM_TRUE;
} );
emscripten_set_mousemove_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL {
const auto scale = EM_ASM_DOUBLE( { return window.devicePixelRatio; } );
ImGui::GetIO().AddMousePosEvent( e->targetX * scale, e->targetY * scale );
tracy::s_wasActive = true;
return EM_TRUE;
} );
emscripten_set_mouseleave_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent*, void* ) -> EM_BOOL {
ImGui::GetIO().AddFocusEvent( false );
tracy::s_wasActive = true;
return EM_TRUE;
} );
emscripten_set_mouseenter_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent*, void* ) -> EM_BOOL {
ImGui::GetIO().AddFocusEvent( true );
tracy::s_wasActive = true;
return EM_TRUE;
} );
emscripten_set_wheel_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenWheelEvent* e, void* ) -> EM_BOOL {
ImGui::GetIO().AddMouseWheelEvent( e->deltaX * -0.05, e->deltaY * -0.05 );
tracy::s_wasActive = true;
return EM_TRUE;
} );
emscripten_set_keydown_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, [] ( int, const EmscriptenKeyboardEvent* e, void* ) -> EM_BOOL {
const auto code = TranslateKeyCode( e->code );
if( code == ImGuiKey_None ) return EM_FALSE;
ImGui::GetIO().AddKeyEvent( code, true );
if( e->key[0] && !e->key[1] ) ImGui::GetIO().AddInputCharacter( *e->key );
return EM_TRUE;
} );
emscripten_set_keyup_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, [] ( int, const EmscriptenKeyboardEvent* e, void* ) -> EM_BOOL {
const auto code = TranslateKeyCode( e->code );
if( code == ImGuiKey_None ) return EM_FALSE;
ImGui::GetIO().AddKeyEvent( code, false );
return EM_TRUE;
} );
s_time = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count();
}
Backend::~Backend()
{
}
void Backend::Show()
{
}
void Backend::Run()
{
emscripten_set_main_loop( []() {
s_redraw();
s_mainThreadTasks->Run();
}, 0, 1 );
}
void Backend::Attention()
{
}
void Backend::NewFrame( int& w, int& h )
{
const auto scale = GetDpiScale();
if( scale != s_prevScale )
{
s_prevScale = scale;
s_scaleChanged( scale );
}
w = EM_ASM_INT( { return window.innerWidth; } ) * scale;
h = EM_ASM_INT( { return window.innerHeight; } ) * scale;
if( s_width != w || s_height != h )
{
EM_ASM( Module.canvas.style.width = window.innerWidth + 'px'; Module.canvas.style.height = window.innerHeight + 'px' );
EM_ASM( Module.canvas.width = $0; Module.canvas.height = $1, w, h );
s_width = w;
s_height = h;
glViewport( 0, 0, s_width, s_height );
tracy::s_wasActive = true;
}
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2( w, h );
io.DisplayFramebufferScale = ImVec2( 1, 1 );
ImGui_ImplOpenGL3_NewFrame();
ImGuiMouseCursor cursor = ImGui::GetMouseCursor();
const char* cursorName;
switch( cursor )
{
case ImGuiMouseCursor_None: cursorName = "none"; break;
case ImGuiMouseCursor_Arrow:
switch( s_isBusy() )
{
default:
case 0: cursorName = "default"; break;
case 1: cursorName = "progress"; break;
case 2: cursorName = "wait"; break;
}
break;
case ImGuiMouseCursor_TextInput: cursorName = "text"; break;
case ImGuiMouseCursor_ResizeAll: cursorName = "move"; break;
case ImGuiMouseCursor_ResizeNS: cursorName = "ns-resize"; break;
case ImGuiMouseCursor_ResizeEW: cursorName = "ew-resize"; break;
case ImGuiMouseCursor_ResizeNESW: cursorName = "nesw-resize"; break;
case ImGuiMouseCursor_ResizeNWSE: cursorName = "nwse-resize"; break;
case ImGuiMouseCursor_Hand: cursorName = "pointer"; break;
case ImGuiMouseCursor_NotAllowed: cursorName = "not-allowed"; break;
default: cursorName = "auto"; break;
};
if( s_prevCursor != cursorName )
{
s_prevCursor = cursorName;
EM_ASM_INT( { document.getElementById('canvas').style.cursor = UTF8ToString($0); }, cursorName );
}
uint64_t time = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count();
io.DeltaTime = std::min( 0.1f, ( time - s_time ) / 1000000.f );
s_time = time;
}
void Backend::EndFrame()
{
const ImVec4 clear_color = ImColor( 20, 20, 17 );
ImGui::Render();
glClearColor( clear_color.x, clear_color.y, clear_color.z, clear_color.w );
glClear( GL_COLOR_BUFFER_BIT );
ImGui_ImplOpenGL3_RenderDrawData( ImGui::GetDrawData() );
}
void Backend::SetIcon( uint8_t* data, int w, int h )
{
}
void Backend::SetTitle( const char* title )
{
EM_ASM( document.title = UTF8ToString($0), title );
}
float Backend::GetDpiScale()
{
return EM_ASM_DOUBLE( { return window.devicePixelRatio; } );
}

View File

@@ -1,216 +0,0 @@
#include <backends/imgui_impl_glfw.h>
#include <backends/imgui_impl_opengl3.h>
#include <backends/imgui_impl_opengl3_loader.h>
#include <chrono>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include "profiler/TracyConfig.hpp"
#include "profiler/TracyImGui.hpp"
#include "Backend.hpp"
#include "RunQueue.hpp"
static GLFWwindow* s_window;
static std::function<void()> s_redraw;
static std::function<void(float)> s_scaleChanged;
static RunQueue* s_mainThreadTasks;
static WindowPosition* s_winPos;
static bool s_iconified;
static float s_prevScale = -1;
extern tracy::Config s_config;
static void glfw_error_callback( int error, const char* description )
{
fprintf(stderr, "Error %d: %s\n", error, description);
}
static void glfw_window_pos_callback( GLFWwindow* window, int x, int y )
{
if( !glfwGetWindowAttrib( window, GLFW_MAXIMIZED ) )
{
s_winPos->x = x;
s_winPos->y = y;
}
}
static void glfw_window_size_callback( GLFWwindow* window, int w, int h )
{
if( !glfwGetWindowAttrib( window, GLFW_MAXIMIZED ) )
{
s_winPos->w = w;
s_winPos->h = h;
}
tracy::s_wasActive = true;
}
static void glfw_window_maximize_callback( GLFWwindow*, int maximized )
{
s_winPos->maximize = maximized;
}
static void glfw_window_iconify_callback( GLFWwindow*, int iconified )
{
s_iconified = iconified != 0;
}
Backend::Backend( const char* title, const std::function<void()>& redraw, const std::function<void(float)>& scaleChanged, const std::function<int(void)>& isBusy, RunQueue* mainThreadTasks )
{
glfwSetErrorCallback( glfw_error_callback );
if( !glfwInit() ) exit( 1 );
#ifdef DISPLAY_SERVER_WAYLAND
glfwWindowHint( GLFW_ALPHA_BITS, 0 );
#else
glfwWindowHint( GLFW_VISIBLE, 0 );
#endif
glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 2 );
glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
#ifdef __APPLE__
glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE );
#endif
#ifdef WIN32
# if GLFW_VERSION_MAJOR > 3 || ( GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 4 )
glfwWindowHint( GLFW_WIN32_KEYBOARD_MENU, 1 );
# endif
# if GLFW_VERSION_MAJOR > 3 || ( GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3 )
glfwWindowHint( GLFW_SCALE_TO_MONITOR, 1 );
# endif
#endif
s_window = glfwCreateWindow( m_winPos.w, m_winPos.h, title, NULL, NULL );
if( !s_window ) exit( 1 );
glfwSetWindowPos( s_window, m_winPos.x, m_winPos.y );
#if GLFW_VERSION_MAJOR > 3 || ( GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 2 )
if( m_winPos.maximize ) glfwMaximizeWindow( s_window );
#endif
glfwMakeContextCurrent( s_window );
glfwSwapInterval( 1 ); // Enable vsync
glfwSetWindowRefreshCallback( s_window, []( GLFWwindow* ) { tracy::s_wasActive = true; s_redraw(); } );
ImGui_ImplGlfw_InitForOpenGL( s_window, true );
ImGui_ImplOpenGL3_Init( "#version 150" );
s_redraw = redraw;
s_scaleChanged = scaleChanged;
s_mainThreadTasks = mainThreadTasks;
s_winPos = &m_winPos;
s_iconified = false;
glfwSetWindowPosCallback( s_window, glfw_window_pos_callback );
glfwSetWindowSizeCallback( s_window, glfw_window_size_callback );
#if GLFW_VERSION_MAJOR > 3 || ( GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3 )
glfwSetWindowMaximizeCallback( s_window, glfw_window_maximize_callback );
#endif
glfwSetWindowIconifyCallback( s_window, glfw_window_iconify_callback );
}
Backend::~Backend()
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
glfwDestroyWindow( s_window );
glfwTerminate();
}
void Backend::Show()
{
glfwShowWindow( s_window );
}
void Backend::Run()
{
while( !glfwWindowShouldClose( s_window ) )
{
if( s_iconified )
{
glfwWaitEvents();
}
else
{
glfwPollEvents();
s_redraw();
if( s_config.focusLostLimit && !glfwGetWindowAttrib( s_window, GLFW_FOCUSED ) ) std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
s_mainThreadTasks->Run();
}
}
}
void Backend::Attention()
{
#if GLFW_VERSION_MAJOR > 3 || ( GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3 )
if( !glfwGetWindowAttrib( s_window, GLFW_FOCUSED ) )
{
glfwRequestWindowAttention( s_window );
}
#endif
}
void Backend::NewFrame( int& w, int& h )
{
const auto scale = GetDpiScale();
if( scale != s_prevScale )
{
s_prevScale = scale;
s_scaleChanged( scale );
}
glfwGetFramebufferSize( s_window, &w, &h );
#if defined( __APPLE__ )
w = static_cast<int>( w / scale );
h = static_cast<int>( h / scale );
#endif
m_w = w;
m_h = h;
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
}
void Backend::EndFrame()
{
const ImVec4 clear_color = ImColor( 20, 20, 17 );
ImGui::Render();
glViewport( 0, 0, m_w, m_h );
glClearColor( clear_color.x, clear_color.y, clear_color.z, clear_color.w );
glClear( GL_COLOR_BUFFER_BIT );
ImGui_ImplOpenGL3_RenderDrawData( ImGui::GetDrawData() );
glfwSwapBuffers( s_window );
}
void Backend::SetIcon( uint8_t* data, int w, int h )
{
GLFWimage icon;
icon.width = w;
icon.height = h;
icon.pixels = data;
glfwSetWindowIcon( s_window, 1, &icon );
}
void Backend::SetTitle( const char* title )
{
glfwSetWindowTitle( s_window, title );
}
float Backend::GetDpiScale()
{
#if GLFW_VERSION_MAJOR > 3 || ( GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3 )
float x, y;
glfwGetWindowContentScale( s_window, &x, &y );
return x;
#else
return 1;
#endif
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,87 +0,0 @@
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include "ConnectionHistory.hpp"
#include "../../server/tracy_pdqsort.h"
#include "profiler/TracyStorage.hpp"
ConnectionHistory::ConnectionHistory()
: m_fn( tracy::GetSavePath( "connection.history" ) )
{
FILE* f = fopen( m_fn.c_str(), "rb" );
if( !f ) return;
uint64_t sz;
if( fread( &sz, 1, sizeof( sz ), f ) != sizeof( sz ) ) goto err;
for( uint64_t i=0; i<sz; i++ )
{
uint64_t ssz, cnt;
if( fread( &ssz, 1, sizeof( ssz ), f ) != sizeof( ssz ) ) goto err;
if( ssz >= 1024 ) goto err;
char tmp[1024];
if( fread( tmp, 1, ssz, f ) != ssz ) goto err;
if( fread( &cnt, 1, sizeof( cnt ), f ) != sizeof( cnt ) ) goto err;
m_connHistMap.emplace( std::string( tmp, tmp+ssz ), cnt );
}
fclose( f );
Rebuild();
return;
err:
fclose( f );
m_connHistMap.clear();
}
ConnectionHistory::~ConnectionHistory()
{
FILE* f = fopen( m_fn.c_str(), "wb" );
if( !f ) return;
uint64_t sz = uint64_t( m_connHistMap.size() );
fwrite( &sz, 1, sizeof( uint64_t ), f );
for( auto& v : m_connHistMap )
{
sz = uint64_t( v.first.size() );
fwrite( &sz, 1, sizeof( uint64_t ), f );
fwrite( v.first.c_str(), 1, sz, f );
fwrite( &v.second, 1, sizeof( v.second ), f );
}
fclose( f );
}
void ConnectionHistory::Rebuild()
{
std::vector<std::unordered_map<std::string, uint64_t>::const_iterator> vec;
vec.reserve( m_connHistMap.size() );
for( auto it = m_connHistMap.begin(); it != m_connHistMap.end(); ++it )
{
vec.emplace_back( it );
}
tracy::pdqsort_branchless( vec.begin(), vec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second > rhs->second; } );
std::swap( m_connHistVec, vec );
}
void ConnectionHistory::Count( const std::string& name )
{
auto it = m_connHistMap.find( name );
if( it != m_connHistMap.end() )
{
it->second++;
}
else
{
m_connHistMap.emplace( name, 1 );
}
Rebuild();
}
void ConnectionHistory::Erase( size_t idx )
{
assert( idx < m_connHistVec.size() );
m_connHistMap.erase( m_connHistVec[idx] );
Rebuild();
}

View File

@@ -1,32 +0,0 @@
#ifndef __CONNECTIONHISTORY_HPP__
#define __CONNECTIONHISTORY_HPP__
#include <stdint.h>
#include <string>
#include <unordered_map>
#include <vector>
class ConnectionHistory
{
public:
ConnectionHistory();
~ConnectionHistory();
const std::string& Name( size_t idx ) const { return m_connHistVec[idx]->first; }
void Count( const std::string& name );
void Erase( size_t idx );
bool empty() const { return m_connHistVec.empty(); }
size_t size() const { return m_connHistVec.size(); }
private:
void Rebuild();
std::string m_fn;
std::unordered_map<std::string, uint64_t> m_connHistMap;
std::vector<std::unordered_map<std::string, uint64_t>::const_iterator> m_connHistVec;
};
#endif

View File

@@ -1,87 +0,0 @@
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include "Filters.hpp"
#include "profiler/TracyStorage.hpp"
Filters::Filters()
: m_fn( tracy::GetSavePath( "client.filters" ) )
{
FILE* f = fopen( m_fn.c_str(), "rb" );
if( !f ) return;
uint8_t sz;
fread( &sz, 1, sizeof( sz ), f );
fread( m_addrFilter.InputBuf, 1, sz, f );
m_addrFilter.Build();
fread( &sz, 1, sizeof( sz ), f );
fread( m_portFilter.InputBuf, 1, sz, f );
m_portFilter.Build();
fread( &sz, 1, sizeof( sz ), f );
fread( m_progFilter.InputBuf, 1, sz, f );
m_progFilter.Build();
fclose( f );
}
Filters::~Filters()
{
FILE* f = fopen( m_fn.c_str(), "wb" );
if( !f ) return;
uint8_t sz = (uint8_t)strlen( m_addrFilter.InputBuf );
fwrite( &sz, 1, sizeof( sz ), f );
fwrite( m_addrFilter.InputBuf, 1, sz, f );
sz = (uint8_t)strlen( m_portFilter.InputBuf );
fwrite( &sz, 1, sizeof( sz ), f );
fwrite( m_portFilter.InputBuf, 1, sz, f );
sz = (uint8_t)strlen( m_progFilter.InputBuf );
fwrite( &sz, 1, sizeof( sz ), f );
fwrite( m_progFilter.InputBuf, 1, sz, f );
fclose( f );
}
void Filters::Clear()
{
m_addrFilter.Clear();
m_portFilter.Clear();
m_progFilter.Clear();
}
void Filters::Draw( float w )
{
m_addrFilter.Draw( "Address filter", w );
m_portFilter.Draw( "Port filter", w );
m_progFilter.Draw( "Program filter", w );
}
bool Filters::IsActive() const
{
return m_addrFilter.IsActive() || m_portFilter.IsActive() || m_progFilter.IsActive();
}
bool Filters::FailAddr( const char* addr )
{
return m_addrFilter.IsActive() && !m_addrFilter.PassFilter( addr );
}
bool Filters::FailPort( uint16_t port )
{
if( !m_portFilter.IsActive() ) return false;
char buf[32];
sprintf( buf, "%" PRIu16, port );
return !m_portFilter.PassFilter( buf );
}
bool Filters::FailProg( const char* prog )
{
return m_progFilter.IsActive() && !m_progFilter.PassFilter( prog );
}

View File

@@ -1,28 +0,0 @@
#ifndef __FILTERS_HPP__
#define __FILTERS_HPP__
#include <imgui.h>
#include <string>
class Filters
{
public:
Filters();
~Filters();
void Clear();
void Draw( float w );
bool IsActive() const;
bool FailAddr( const char* addr );
bool FailPort( uint16_t port );
bool FailProg( const char* prog );
private:
std::string m_fn;
ImGuiTextFilter m_addrFilter, m_portFilter, m_progFilter;
};
#endif

View File

@@ -1,59 +0,0 @@
#include <imgui.h>
#include <math.h>
#include <backends/imgui_impl_opengl3.h>
#include <misc/freetype/imgui_freetype.h>
#include "Fonts.hpp"
#include "profiler/IconsFontAwesome6.h"
#include "font/DroidSans.hpp"
#include "font/FiraCodeRetina.hpp"
#include "font/FontAwesomeSolid.hpp"
ImFont* s_bigFont;
ImFont* s_smallFont;
ImFont* s_fixedWidth;
void LoadFonts( float scale )
{
static const ImWchar rangesBasic[] = {
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x03BC, 0x03BC, // micro
0x03C3, 0x03C3, // small sigma
0x2013, 0x2013, // en dash
0x2026, 0x2026, // ellipsis
0x2264, 0x2264, // less-than or equal to
0,
};
static const ImWchar rangesIcons[] = {
ICON_MIN_FA, ICON_MAX_FA,
0
};
static const ImWchar rangesFixed[] = {
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x2026, 0x2026, // ellipsis
0
};
ImGuiIO& io = ImGui::GetIO();
ImFontConfig configBasic;
configBasic.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_LightHinting;
ImFontConfig configMerge;
configMerge.MergeMode = true;
configMerge.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_LightHinting;
ImFontConfig configFixed;
configFixed.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_LightHinting;
configFixed.GlyphExtraAdvanceX = -1;
io.Fonts->Clear();
io.Fonts->AddFontFromMemoryCompressedTTF( tracy::DroidSans_compressed_data, tracy::DroidSans_compressed_size, round( 15.0f * scale ), &configBasic, rangesBasic );
io.Fonts->AddFontFromMemoryCompressedTTF( tracy::FontAwesomeSolid_compressed_data, tracy::FontAwesomeSolid_compressed_size, round( 14.0f * scale ), &configMerge, rangesIcons );
s_fixedWidth = io.Fonts->AddFontFromMemoryCompressedTTF( tracy::FiraCodeRetina_compressed_data, tracy::FiraCodeRetina_compressed_size, round( 15.0f * scale ), &configFixed, rangesFixed );
s_bigFont = io.Fonts->AddFontFromMemoryCompressedTTF( tracy::DroidSans_compressed_data, tracy::DroidSans_compressed_size, round( 21.0f * scale ), &configBasic );
io.Fonts->AddFontFromMemoryCompressedTTF( tracy::FontAwesomeSolid_compressed_data, tracy::FontAwesomeSolid_compressed_size, round( 20.0f * scale ), &configMerge, rangesIcons );
s_smallFont = io.Fonts->AddFontFromMemoryCompressedTTF( tracy::DroidSans_compressed_data, tracy::DroidSans_compressed_size, round( 10.0f * scale ), &configBasic );
ImGui_ImplOpenGL3_DestroyFontsTexture();
ImGui_ImplOpenGL3_CreateFontsTexture();
}

View File

@@ -1,12 +0,0 @@
#ifndef __FONTS_HPP__
#define __FONTS_HPP__
struct ImFont;
extern ImFont* s_bigFont;
extern ImFont* s_smallFont;
extern ImFont* s_fixedWidth;
void LoadFonts( float scale );
#endif

View File

@@ -1,134 +0,0 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../public/common/TracySocket.hpp"
#include "../public/common/TracyVersion.hpp"
#include "GitRef.hpp"
#include "HttpRequest.hpp"
#if defined _WIN32
# include <windows.h>
extern "C" typedef LONG (WINAPI *t_RtlGetVersion)( PRTL_OSVERSIONINFOW );
extern "C" typedef char* (WINAPI *t_WineGetVersion)();
extern "C" typedef char* (WINAPI *t_WineGetBuildId)();
#elif defined __linux__
# include <sys/utsname.h>
#elif defined __APPLE__
# include "TargetConditionals.h"
#endif
static constexpr char CRLF[2] = { '\r', '\n' };
static const char* GetOsInfo()
{
static char buf[1024];
#if defined _WIN32
t_RtlGetVersion RtlGetVersion = (t_RtlGetVersion)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlGetVersion" );
if( !RtlGetVersion )
{
# ifdef __MINGW32__
sprintf( buf, "Windows (MingW)" );
# else
sprintf( buf, "Windows" );
# endif
}
else
{
RTL_OSVERSIONINFOW ver = { sizeof( RTL_OSVERSIONINFOW ) };
RtlGetVersion( &ver );
# ifdef __MINGW32__
sprintf( buf, "Windows %i.%i.%i (MingW)", (int)ver.dwMajorVersion, (int)ver.dwMinorVersion, (int)ver.dwBuildNumber );
# else
auto WineGetVersion = (t_WineGetVersion)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "wine_get_version" );
auto WineGetBuildId = (t_WineGetBuildId)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "wine_get_build_id" );
if( WineGetVersion && WineGetBuildId )
{
sprintf( buf, "Windows %i.%i.%i (Wine %s [%s])", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, WineGetVersion(), WineGetBuildId() );
}
else
{
sprintf( buf, "Windows %i.%i.%i", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber );
}
# endif
}
#elif defined __linux__
struct utsname utsName;
uname( &utsName );
# if defined __ANDROID__
sprintf( buf, "Linux %s (Android)", utsName.release );
# else
sprintf( buf, "Linux %s", utsName.release );
# endif
#elif defined __APPLE__
# if TARGET_OS_IPHONE == 1
sprintf( buf, "Darwin (iOS)" );
# elif TARGET_OS_MAC == 1
sprintf( buf, "Darwin (OSX)" );
# else
sprintf( buf, "Darwin (unknown)" );
# endif
#elif defined __DragonFly__
sprintf( buf, "BSD (DragonFly)" );
#elif defined __FreeBSD__
sprintf( buf, "BSD (FreeBSD)" );
#elif defined __NetBSD__
sprintf( buf, "BSD (NetBSD)" );
#elif defined __OpenBSD__
sprintf( buf, "BSD (OpenBSD)" );
#elif defined __QNX__
sprintf( buf, "QNX" );
#else
sprintf( buf, "unknown" );
#endif
return buf;
}
void HttpRequest( const char* server, const char* resource, int port, const std::function<void(int, char*)>& cb )
{
tracy::Socket sock;
if( !sock.ConnectBlocking( server, port ) ) return;
char request[4096];
const auto len = sprintf( request, "GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: Tracy Profiler %i.%i.%i (%s) [%s]\r\nConnection: close\r\nCache-Control: no-cache, no-store, must-revalidate\r\n\r\n", resource, server, tracy::Version::Major, tracy::Version::Minor, tracy::Version::Patch, GetOsInfo(), tracy::GitRef );
sock.Send( request, len );
char response[4096];
const auto sz = sock.ReadUpTo( response, 4096 );
if( sz < 13 ) return;
if( memcmp( response, "HTTP/1.1 200", 12 ) != 0 ) return;
auto hdr = response + 13;
int contentLength = 0;
for(;;)
{
while( memcmp( hdr, CRLF, 2 ) != 0 ) hdr++;
hdr += 2;
if( memcmp( hdr, "Content-Length: ", 16 ) == 0 )
{
hdr += 16;
contentLength = atoi( hdr );
break;
}
}
assert( contentLength != 0 );
for(;;)
{
while( memcmp( hdr, CRLF, 2 ) != 0 ) hdr++;
hdr += 2;
if( memcmp( hdr, CRLF, 2 ) == 0 )
{
hdr += 2;
break;
}
hdr += 2;
}
const auto hdrSize = hdr - response;
const auto partSize = sz - hdrSize;
char* data = new char[contentLength];
memcpy( data, hdr, partSize );
auto remaining = contentLength - partSize;
if( remaining > 0 ) sock.Read( data + partSize, remaining, 15 );
cb( contentLength, data );
}

View File

@@ -1,8 +0,0 @@
#ifndef __HTTPREQUEST_HPP__
#define __HTTPREQUEST_HPP__
#include <functional>
void HttpRequest( const char* server, const char* resource, int port, const std::function<void(int, char*)>& cb );
#endif

View File

@@ -1,20 +0,0 @@
#include <imgui.h>
#include "profiler/TracyStorage.hpp"
#include "ImGuiContext.hpp"
ImGuiTracyContext::ImGuiTracyContext()
: m_iniFilename( tracy::GetSavePath( "imgui.ini" ) )
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = m_iniFilename.c_str();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_DockingEnable;
io.ConfigInputTextCursorBlink = false;
io.ConfigScrollbarScrollByPage = false;
}
ImGuiTracyContext::~ImGuiTracyContext()
{
ImGui::DestroyContext();
}

View File

@@ -1,16 +0,0 @@
#ifndef __IMGUICONTEXT_HPP__
#define __IMGUICONTEXT_HPP__
#include <string>
class ImGuiTracyContext
{
public:
ImGuiTracyContext();
~ImGuiTracyContext();
private:
std::string m_iniFilename;
};
#endif

View File

@@ -1,41 +0,0 @@
#include "IsElevated.hpp"
#ifdef _WIN32
#include <windows.h>
bool IsElevated()
{
HANDLE token;
if( OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &token ) == 0 ) return false;
TOKEN_ELEVATION te;
DWORD sz;
if( GetTokenInformation( token, TokenElevation, &te, sizeof( te ), &sz ) == 0 )
{
CloseHandle( token );
return false;
}
bool ret = te.TokenIsElevated;
CloseHandle( token );
return ret;
}
#elif defined __EMSCRIPTEN__
bool IsElevated()
{
return false;
}
#else
#include <unistd.h>
bool IsElevated()
{
return getuid() == 0;
}
#endif

View File

@@ -1,6 +0,0 @@
#ifndef __ISELEVATED_HPP__
#define __ISELEVATED_HPP__
bool IsElevated();
#endif

View File

@@ -1,65 +0,0 @@
#ifdef _WIN32
# include <ws2tcpip.h>
#else
# ifdef __FreeBSD__
# include <netinet/in.h>
# endif
# include <arpa/inet.h>
# include <sys/socket.h>
# include <netdb.h>
#endif
#include "ResolvService.hpp"
ResolvService::ResolvService( uint16_t port )
: m_exit( false )
, m_port( port )
#ifndef __EMSCRIPTEN__
, m_thread( [this] { Worker(); } )
#endif
{
}
ResolvService::~ResolvService()
{
#ifndef __EMSCRIPTEN__
m_exit.store( true, std::memory_order_relaxed );
m_cv.notify_one();
m_thread.join();
#endif
}
void ResolvService::Query( uint32_t ip, const std::function<void(std::string&&)>& callback )
{
#ifndef __EMSCRIPTEN__
std::lock_guard<std::mutex> lock( m_lock );
m_queue.emplace_back( QueueItem { ip, callback } );
m_cv.notify_one();
#endif
}
void ResolvService::Worker()
{
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons( m_port );
char buf[128];
for(;;)
{
std::unique_lock<std::mutex> lock( m_lock );
m_cv.wait( lock, [this] { return !m_queue.empty() || m_exit.load( std::memory_order_relaxed ); } );
if( m_exit.load( std::memory_order_relaxed ) ) return;
auto query = m_queue.back();
m_queue.pop_back();
lock.unlock();
addr.sin_addr.s_addr = query.ip;
if( getnameinfo( (const struct sockaddr*)&addr, sizeof( sockaddr_in ), buf, 128, nullptr, 0, NI_NOFQDN ) != 0 )
{
inet_ntop( AF_INET, &query.ip, buf, 17 );
}
query.callback( buf );
}
}

View File

@@ -1,38 +0,0 @@
#ifndef __RESOLVSERVICE_HPP__
#define __RESOLVSERVICE_HPP__
#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <stdint.h>
#include <string>
#include <thread>
#include <vector>
class ResolvService
{
struct QueueItem
{
uint32_t ip;
std::function<void(std::string&&)> callback;
};
public:
ResolvService( uint16_t port );
~ResolvService();
void Query( uint32_t ip, const std::function<void(std::string&&)>& callback );
private:
void Worker();
std::atomic<bool> m_exit;
std::mutex m_lock;
std::condition_variable m_cv;
std::vector<QueueItem> m_queue;
uint16_t m_port;
std::thread m_thread;
};
#endif

View File

@@ -1,31 +0,0 @@
#include "RunQueue.hpp"
RunQueue::RunQueue()
: m_mainThread( std::this_thread::get_id() )
{
}
void RunQueue::Queue( const std::function<void()>& cb, bool forceDelay )
{
if( !forceDelay && std::this_thread::get_id() == m_mainThread )
{
cb();
}
else
{
std::lock_guard<std::mutex> lock( m_lock );
m_queue.emplace_back( cb );
}
}
void RunQueue::Run()
{
std::unique_lock<std::mutex> lock( m_lock );
if( !m_queue.empty() )
{
std::vector<std::function<void()>> tmp;
std::swap( tmp, m_queue );
lock.unlock();
for( auto& cb : tmp ) cb();
}
}

View File

@@ -1,23 +0,0 @@
#ifndef __RUNQUEUE_HPP__
#define __RUNQUEUE_HPP__
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
class RunQueue
{
public:
RunQueue();
void Queue( const std::function<void()>& cb, bool forceDelay = false );
void Run();
private:
std::vector<std::function<void()>> m_queue;
std::mutex m_lock;
std::thread::id m_mainThread;
};
#endif

View File

@@ -1,48 +0,0 @@
#include <stdint.h>
#include <stdio.h>
#include "WindowPosition.hpp"
#include "profiler/TracyStorage.hpp"
WindowPosition::WindowPosition()
: m_fn( tracy::GetSavePath( "window.position" ) )
{
Defaults();
FILE* f = fopen( m_fn.c_str(), "rb" );
if( f )
{
uint32_t data[5];
if( fread( data, 1, sizeof( data ), f ) == sizeof( data ) )
{
x = data[0];
y = data[1];
w = data[2];
h = data[3];
maximize = data[4];
}
fclose( f );
if( w <= 0 || h <= 0 ) Defaults();
}
}
WindowPosition::~WindowPosition()
{
FILE* f = fopen( m_fn.c_str(), "wb" );
if( !f ) return;
uint32_t data[5] = { uint32_t( x ), uint32_t( y ), uint32_t( w ), uint32_t( h ), uint32_t( maximize ) };
fwrite( data, 1, sizeof( data ), f );
fclose( f );
}
void WindowPosition::Defaults()
{
x = 200;
y = 200;
w = 1650;
h = 960;
maximize = 0;
}

View File

@@ -1,20 +0,0 @@
#ifndef __WINDOWPOSITION_HPP__
#define __WINDOWPOSITION_HPP__
#include <string>
class WindowPosition
{
public:
WindowPosition();
~WindowPosition();
int x, y, w, h, maximize;
private:
void Defaults();
std::string m_fn;
};
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
// File: 'icon.png' (854 bytes)
// Exported using binary_to_compressed_c.cpp
static const unsigned int Icon_size = 854;
static const unsigned int Icon_data[856/4] =
{
0x474e5089, 0x0a1a0a0d, 0x0d000000, 0x52444849, 0x00010000, 0x00010000, 0x00000308, 0x58ac6b00, 0x00000054, 0x59487009, 0x0a000073, 0x0a000000,
0x82430100, 0x00003ccc, 0x42730300, 0x08085449, 0x4fe1db08, 0x000000e0, 0x544c50b4, 0x00000045, 0x00000000, 0x00000000, 0xffe4d600, 0xb9f1d1bd,
0xc8b0efcf, 0xe8c5adea, 0x9de7c4ab, 0xb191dfb9, 0xcda27ed8, 0x72c99e77, 0x9a72c79a, 0xbc8b5fc6, 0x56bc8b5e, 0x713cb684, 0xa67039a7, 0x35a66e37,
0x6b34a46d, 0xa36b33a3, 0x009d642a, 0x6126d66e, 0xcc69009b, 0x00cb6800, 0x6300c364, 0xbc6000c1, 0x19ba6000, 0x5b009458, 0xab5800b1, 0x00a95700,
0x5300a655, 0x8e4f0ea0, 0x008e4f0d, 0x5100a052, 0x9d51009e, 0x00994e00, 0x4d00974d, 0x944c0096, 0x00914b00, 0x4700914a, 0x8a47008b, 0x00894700,
0x45008946, 0x14121186, 0x01110f0e, 0x00000101, 0x56a20400, 0x0000006f, 0x4e527404, 0x521b0053, 0xb06c3055, 0x020000a6, 0x41444929, 0xedda7854,
0x2c95c5db, 0xc1441449, 0x66666a62, 0xf5fd6666, 0xe7ce5a9a, 0xc4dce8c8, 0xd822bb2b, 0x24378fd6, 0x3db35249, 0x027f3561, 0x584d7d68, 0x0000002d,
0x00000000, 0x005f8000, 0xf7023f3e, 0xdef00099, 0xdffbdc09, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x0587f400, 0xe011afd6, 0x0ec0adf0, 0x700004eb, 0xb7585fbb, 0x6e5c0335, 0x3e65d616, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0xac2d3c78, 0x0c013ac7, 0xd9e75b23, 0xf3b37c5d, 0x01b2b6ec, 0x9beded6a, 0xbf8f3f15, 0xce3db801, 0xc00436ee, 0x277666c9, 0x000001a5,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xa39b8000, 0x078a6eec, 0xe8008c30, 0xb30ee6f5, 0x4af5eb9b, 0xf3371807, 0xa74cdd96,
0xeae00313, 0x40aba32c, 0x199bdb00, 0x0009976d, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0e000000, 0x883a32f6, 0x01186001,
0x568d1dd0, 0x195a3236, 0x0021ba8d, 0x5fe61dc6, 0xbc00a35f, 0x25ec543c, 0x66e5c018, 0x05cb2ec5, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0xbb153bd7, 0x1860040e, 0xbb3d3001, 0x85b15ab0, 0x0038e9d9, 0x2adea6e3, 0x00e9d536, 0xbbb00608, 0xe16edc99, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x53f3c000, 0x9186cf72, 0xc5830070, 0xe4b5a5c5, 0x07171696, 0xadc600c1, 0xdeb725bf,
0x1e200719, 0x524efdc0, 0xee0161f7, 0x03ba9336, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000000, 0x92ef5e00, 0xc48c357a,
0x4e5f9804, 0x804cbe6e, 0x00000202, 0x00000000, 0x00000000, 0x00000000, 0x0000043f, 0x00000000, 0x00000000, 0xe047e7c0, 0xe200133e, 0x00000007,
0x00000000, 0xcd43f000, 0x6cd584f6, 0x24927c4c, 0x8a1907fd, 0xabe6918e, 0x00003480, 0x45490000, 0x42ae444e, 0x00008260,
};

View File

@@ -1,274 +0,0 @@
/**
* Copyright (c) 2016 rxi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ini.h"
struct ini_t {
char *data;
char *end;
};
/* Case insensitive string compare */
static int strcmpci(const char *a, const char *b) {
for (;;) {
int d = tolower(*a) - tolower(*b);
if (d != 0 || !*a) {
return d;
}
a++, b++;
}
}
/* Returns the next string in the split data */
static char* next(ini_t *ini, char *p) {
p += strlen(p);
while (p < ini->end && *p == '\0') {
p++;
}
return p;
}
static void trim_back(ini_t *ini, char *p) {
while (p >= ini->data && (*p == ' ' || *p == '\t' || *p == '\r')) {
*p-- = '\0';
}
}
static char* discard_line(ini_t *ini, char *p) {
while (p < ini->end && *p != '\n') {
*p++ = '\0';
}
return p;
}
static char *unescape_quoted_value(ini_t *ini, char *p) {
/* Use `q` as write-head and `p` as read-head, `p` is always ahead of `q`
* as escape sequences are always larger than their resultant data */
char *q = p;
p++;
while (p < ini->end && *p != '"' && *p != '\r' && *p != '\n') {
if (*p == '\\') {
/* Handle escaped char */
p++;
switch (*p) {
default : *q = *p; break;
case 'r' : *q = '\r'; break;
case 'n' : *q = '\n'; break;
case 't' : *q = '\t'; break;
case '\r' :
case '\n' :
case '\0' : goto end;
}
} else {
/* Handle normal char */
*q = *p;
}
q++, p++;
}
end:
return q;
}
/* Splits data in place into strings containing section-headers, keys and
* values using one or more '\0' as a delimiter. Unescapes quoted values */
static void split_data(ini_t *ini) {
char *value_start, *line_start;
char *p = ini->data;
while (p < ini->end) {
switch (*p) {
case '\r':
case '\n':
case '\t':
case ' ':
*p = '\0';
/* Fall through */
case '\0':
p++;
break;
case '[':
p += strcspn(p, "]\n");
*p = '\0';
break;
case ';':
p = discard_line(ini, p);
break;
default:
line_start = p;
p += strcspn(p, "=\n");
/* Is line missing a '='? */
if (*p != '=') {
p = discard_line(ini, line_start);
break;
}
trim_back(ini, p - 1);
/* Replace '=' and whitespace after it with '\0' */
do {
*p++ = '\0';
} while (*p == ' ' || *p == '\r' || *p == '\t');
/* Is a value after '=' missing? */
if (*p == '\n' || *p == '\0') {
p = discard_line(ini, line_start);
break;
}
if (*p == '"') {
/* Handle quoted string value */
value_start = p;
p = unescape_quoted_value(ini, p);
/* Was the string empty? */
if (p == value_start) {
p = discard_line(ini, line_start);
break;
}
/* Discard the rest of the line after the string value */
p = discard_line(ini, p);
} else {
/* Handle normal value */
p += strcspn(p, "\n");
trim_back(ini, p - 1);
}
break;
}
}
}
ini_t* ini_load(const char *filename) {
ini_t *ini = NULL;
FILE *fp = NULL;
int n, sz;
/* Init ini struct */
ini = malloc(sizeof(*ini));
if (!ini) {
goto fail;
}
memset(ini, 0, sizeof(*ini));
/* Open file */
fp = fopen(filename, "rb");
if (!fp) {
goto fail;
}
/* Get file size */
fseek(fp, 0, SEEK_END);
sz = ftell(fp);
rewind(fp);
/* Load file content into memory, null terminate, init end var */
ini->data = malloc(sz + 1);
ini->data[sz] = '\0';
ini->end = ini->data + sz;
n = fread(ini->data, 1, sz, fp);
if (n != sz) {
goto fail;
}
/* Prepare data */
split_data(ini);
/* Clean up and return */
fclose(fp);
return ini;
fail:
if (fp) fclose(fp);
if (ini) ini_free(ini);
return NULL;
}
void ini_free(ini_t *ini) {
free(ini->data);
free(ini);
}
const char* ini_get(ini_t *ini, const char *section, const char *key) {
char *current_section = "";
char *val;
char *p = ini->data;
if (*p == '\0') {
p = next(ini, p);
}
while (p < ini->end) {
if (*p == '[') {
/* Handle section */
current_section = p + 1;
} else {
/* Handle key */
val = next(ini, p);
if (!section || !strcmpci(section, current_section)) {
if (!strcmpci(p, key)) {
return val;
}
}
p = val;
}
p = next(ini, p);
}
return NULL;
}
int ini_sget(
ini_t *ini, const char *section, const char *key,
const char *scanfmt, void *dst
) {
const char *val = ini_get(ini, section, key);
if (!val) {
return 0;
}
if (scanfmt) {
sscanf(val, scanfmt, dst);
} else {
*((const char**) dst) = val;
}
return 1;
}

View File

@@ -1,28 +0,0 @@
/**
* Copyright (c) 2016 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See `ini.c` for details.
*/
#ifndef INI_H
#define INI_H
#define INI_VERSION "0.1.1"
#if defined(__cplusplus)
extern "C" {
#endif
typedef struct ini_t ini_t;
ini_t* ini_load(const char *filename);
void ini_free(ini_t *ini);
const char* ini_get(ini_t *ini, const char *section, const char *key);
int ini_sget(ini_t *ini, const char *section, const char *key, const char *scanfmt, void *dst);
#if defined(__cplusplus)
}
#endif
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,202 +0,0 @@
#include "IconsFontAwesome6.h"
#include "TracyAchievements.hpp"
#include "TracyImGui.hpp"
#include "TracySourceContents.hpp"
#include "TracyWeb.hpp"
namespace tracy::data
{
AchievementItem ai_samplingIntro = { "samplingIntro", "Sampling program execution", [](const ctx& c){
ImGui::TextWrapped( "Sampling program execution is a great way to find out where the hot spots are in your program. It can be used to find out which functions take the most time, or which lines of code are executed the most often." );
ImGui::TextWrapped( "While instrumentation requires changes to your code, sampling does not. However, because of the way it works, the results are coarser and it's not possible to know when functions are called or when they return." );
ImGui::TextWrapped( "Sampling is automatic on Linux. On Windows, you must run the profiled application as an administrator for it to work." );
ImGui::PushFont( c.small );
ImGui::PushStyleColor( ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled] );
ImGui::TextWrapped( "Depending on your system configuration, some additional steps may be required. Please refer to the user manual for more information." );
ImGui::PopStyleColor();
ImGui::PopFont();
} };
AchievementItem* ac_samplingItems[] = { &ai_samplingIntro, nullptr };
AchievementCategory ac_sampling = { "sampling", "Sampling", ac_samplingItems };
AchievementItem ai_100million = { "100million", "It's over 100 million!", [](const ctx& c){
ImGui::TextWrapped( "Tracy can handle a lot of data. How about 100 million zones in a single trace? Add a lot of zones to your program and see how it handles it!" );
ImGui::TextWrapped( "Capturing a long-running profile trace is easy. Need to profile an hour of your program execution? You can do it." );
ImGui::TextWrapped( "Note that it doesn't make much sense to instrument every little function you might have. The cost of the instrumentation itself will be higher than the cost of the function in such a case." );
ImGui::PushFont( c.small );
ImGui::PushStyleColor( ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled] );
ImGui::TextWrapped( "Keep in mind that the more zones you have, the more memory and CPU time the profiler will use. Be careful not to run out of memory." );
ImGui::TextWrapped( "To capture 100 million zones, you will need approximately 4 GB of RAM." );
ImGui::PopStyleColor();
ImGui::PopFont();
} };
AchievementItem ai_instrumentationStatistics = { "instrumentationStatistics", "Show me the stats!", [](const ctx&){
ImGui::TextWrapped( "Once you have instrumented your application, you can view the statistics for each zone in the timeline. This allows you to see how much time is spent in each zone and how many times it is called." );
ImGui::TextWrapped( "To view the statistics, click on the \"" ICON_FA_ARROW_UP_WIDE_SHORT " Statistics\" button on the top bar. This will open a new window with a list of all zones in the trace." );
} };
AchievementItem ai_findZone = { "findZone", "Find some zones", [](const ctx&){
ImGui::TextWrapped( "You can search for zones in the trace by opening the search window with the \"" ICON_FA_MAGNIFYING_GLASS " Find zone\" button on the top bar. It will ask you for the zone name, which in most cases will be the function name in the code." );
ImGui::TextWrapped( "The search may find more than one zone with the same name. A list of all the zones found is displayed, and you can select any of them." );
ImGui::TextWrapped( "Alternatively, you can open the Statistics window and click an entry there. This will open the Find zone window as if you had searched for that zone." );
ImGui::TextWrapped( "When a zone is selected, a number of statistics are displayed to help you understand the performance of your application. In addition, a histogram of the zone execution times is displayed to make it easier for you to determine the performance of the profiled code. Be sure to select a zone with a large number of calls to make the histogram look interesting!" );
ImGui::TextWrapped( "Note that you can draw a range on the histogram to limit the number of entries displayed in the zone list below. This list allows you to examine each zone individually. There are also a number of zone groupings that you can select. Each group can be selected and the time associated with the selected group will be highlighted on the histogram." );
} };
AchievementItem* ac_instrumentationIntroItems[] = {
&ai_100million,
&ai_instrumentationStatistics,
&ai_findZone,
nullptr
};
AchievementItem ai_instrumentationIntro = { "instrumentationIntro", "Instrumentating your application", [](const ctx& c){
constexpr const char* src = R"(#include "Tracy.hpp"
void SomeFunction()
{
ZoneScoped;
// Your code here
}
)";
static SourceContents sc;
sc.Parse( src );
ImGui::TextWrapped( "Instrumentation is a powerful feature that allows you to see the exact runtime of each call to the selected set of functions. The downside is that it takes a bit of manual work to get it set up." );
ImGui::TextWrapped( "To get started, open a source file and include the Tracy.hpp header. This will give you access to a variety of macros provided by Tracy. Next, add the ZoneScoped macro to the beginning of one of your functions, like this:" );
ImGui::PushFont( c.fixed );
PrintSource( sc.get() );
ImGui::PopFont();
ImGui::TextWrapped( "Now, when you profile your application, you will see a new zone appear on the timeline for each call to the function. This allows you to see how much time is spent in each call and how many times the function is called." );
ImGui::PushFont( c.small );
ImGui::PushStyleColor( ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled] );
ImGui::TextWrapped( "Note: The ZoneScoped macro is just one of the many macros provided by Tracy. See the documentation for more information." );
ImGui::TextWrapped( "The above description applies to C++ code, but things are done similarly in other programming languages. Refer to the documentation for your language for more information." );
ImGui::PopStyleColor();
ImGui::PopFont();
}, ac_instrumentationIntroItems };
AchievementItem ai_frameImages = { "frameImages", "A picture is worth a thousand words", [](const ctx&){
ImGui::TextWrapped( "Tracy allows you to add context to each frame, by attaching a screenshot. You can do this with the FrameImage macro." );
ImGui::TextWrapped( "You will have to do the screen capture and resizing yourself, which can be a bit complicated. The manual provides a sample code that shows how to do this in a performant way." );
ImGui::TextWrapped( "The frame images are displayed in the context of a frame, for example, when you hover over the frame in the timeline or in the frame graph at the top of the screen." );
ImGui::TextWrapped( "You can even view a recording of what your application was doing by clicking the " ICON_FA_SCREWDRIVER_WRENCH " icon and then selecting the \"" ICON_FA_PLAY " Playback\" option. Try it out!" );
ImGui::TextWrapped( "The FrameImage macro is a great way to see what happened in your application at a particular time. Maybe you have a performance problem that only occurs when a certain object is on the screen?" );
} };
AchievementItem* ac_instrumentFramesItems[] = {
&ai_frameImages,
nullptr
};
AchievementItem ai_instrumentFrames = { "instrumentFrames", "Instrumenting frames", [](const ctx& c){
constexpr const char* src = R"(#include "Tracy.hpp"
void Render()
{
// Render the frame
SwapBuffers();
FrameMark;
}
)";
static SourceContents sc;
sc.Parse( src );
ImGui::TextWrapped( "In addition to instrumenting functions, you can also instrument frames. This allows you to see how much time is spent in each frame of your application." );
ImGui::TextWrapped( "To instrument frames, you need to add the FrameMark macro at the beginning of each frame. This can be done in the main loop of your application, or in a separate function that is called at the beginning of each frame." );
ImGui::PushFont( c.fixed );
PrintSource( sc.get() );
ImGui::PopFont();
ImGui::TextWrapped( "When you profile your application, you will see a new frame appear on the timeline each time the FrameMark macro is called. This allows you to see how much time is spent in each frame and how many frames are rendered per second." );
ImGui::TextWrapped( "The FrameMark macro is a great way to see at a glance how your application is performing over time. Maybe there are some performance problems that only appear after a few minutes of running the application? A frame graph is drawn at the top of the profiler window where you can see the timing of all frames." );
ImGui::TextWrapped( "Note that some applications do not have a frame-based structure, and in such cases, frame instrumentation may not be useful. That's ok." );
}, ac_instrumentFramesItems };
AchievementItem* ac_instrumentationItems[] = { &ai_instrumentationIntro, &ai_instrumentFrames, nullptr };
AchievementCategory ac_instrumentation = { "instrumentation", "Instrumentation", ac_instrumentationItems };
AchievementItem ai_loadTrace = { "loadTrace", "Load a trace", [](const ctx&){
ImGui::TextWrapped( "You can open a previously saved trace file (or one received from a friend) with the \"" ICON_FA_FOLDER_OPEN " Open saved trace\" button on the welcome screen." );
} };
AchievementItem ai_saveTrace = { "saveTrace", "Save a trace", [](const ctx&){
ImGui::TextWrapped( "Now that you have traced your application (or are in the process of doing so), you can save it to disk for future reference. You can do this by clicking on the " ICON_FA_WIFI " icon in the top left corner of the screen and then clicking on the \"" ICON_FA_FLOPPY_DISK " Save trace\" button." );
ImGui::TextWrapped( "Keeping old traces on hand can be beneficial, as you can compare the performance of your optimizations with what you had before." );
ImGui::TextWrapped( "You can also share the trace with your friends or co-workers by sending them the trace file." );
ImGui::Spacing();
tracy::TextColoredUnformatted( 0xFF44FFFF, ICON_FA_TRIANGLE_EXCLAMATION );
ImGui::SameLine();
ImGui::TextUnformatted( "Warning" );
ImGui::SameLine();
tracy::TextColoredUnformatted( 0xFF44FFFF, ICON_FA_TRIANGLE_EXCLAMATION );
ImGui::TextWrapped( "Trace files can contain sensitive information about your application, such as program code, or even the contents of source files. Be careful when sharing them with others." );
} };
AchievementItem* ac_connectToServerItems[] = {
&ai_saveTrace,
&ai_loadTrace,
nullptr
};
AchievementItem* ac_connectToServerUnlock[] = {
&ai_instrumentationIntro,
&ai_samplingIntro,
nullptr
};
AchievementItem ai_connectToServer = { "connectToClient", "First profiling session", [](const ctx&){
ImGui::TextWrapped( "Let's start our adventure by instrumenting your application and connecting it to the profiler. Here's a quick refresher:" );
ImGui::TextWrapped( " 1. Integrate Tracy Profiler into your application. This can be done using CMake, Meson, or simply by adding the source files to your project." );
ImGui::TextWrapped( " 2. Make sure that TracyClient.cpp (or the Tracy library) is included in your build." );
ImGui::TextWrapped( " 3. Define TRACY_ENABLE in your build configuration, for the whole application. Do not do it in a single source file because it won't work." );
ImGui::TextWrapped( " 4. Start your application, and \"" ICON_FA_WIFI " Connect\" to it with the profiler." );
ImGui::TextWrapped( "Please refer to the user manual for more details." );
if( ImGui::SmallButton( "Download the user manual" ) )
{
tracy::OpenWebpage( "https://github.com/wolfpld/tracy/releases" );
}
}, ac_connectToServerItems, ac_connectToServerUnlock };
AchievementItem ai_globalSettings = { "globalSettings", "Global settings", [](const ctx&){
ImGui::TextWrapped( "Tracy has a variety of settings that can be adjusted to suit your needs. These settings can be found by clicking on the " ICON_FA_WRENCH " icon on the welcome screen. This will open the about window, where you can expand the \"" ICON_FA_TOOLBOX " Global settings\" menu." );
ImGui::TextWrapped( "The settings are saved between sessions, so you only need to set them once." );
} };
AchievementItem* ac_achievementsIntroItems[] = {
&ai_connectToServer,
&ai_globalSettings,
nullptr
};
AchievementItem ai_achievementsIntro = { "achievementsIntro", "Click here to discover achievements!", [](const ctx&){
ImGui::TextWrapped( "Clicking on the " ICON_FA_STAR " button opens the Achievements List. Here you can see the tasks to be completed along with a short description of what needs to be done." );
ImGui::TextWrapped( "As you complete each Achievement, new Achievements will appear, so be sure to keep checking the list for new ones!" );
ImGui::TextWrapped( "To make the new things easier to spot, the Achievements List will show a marker next to them. The achievements " ICON_FA_STAR " button will glow yellow when there are new things to see." );
ImGui::TextUnformatted( "New tasks:" );
ImGui::SameLine();
TextColoredUnformatted( 0xFF4488FF, ICON_FA_CIRCLE_EXCLAMATION );
ImGui::TextUnformatted( "Completed tasks:" );
ImGui::SameLine();
TextColoredUnformatted( 0xFF44FF44, ICON_FA_CIRCLE_CHECK );
ImGui::TextWrapped( "Good luck!" );
}, ac_achievementsIntroItems, nullptr, true, 1 };
AchievementItem* ac_firstStepsItems[] = { &ai_achievementsIntro, nullptr };
AchievementCategory ac_firstSteps = { "firstSteps", "First steps", ac_firstStepsItems, 1 };
AchievementCategory* AchievementCategories[] = {
&ac_firstSteps,
&ac_instrumentation,
&ac_sampling,
nullptr
};
}

View File

@@ -1,225 +0,0 @@
#include <assert.h>
#include <inttypes.h>
#include <time.h>
#include "../ini.h"
#include "TracyAchievements.hpp"
#include "TracyStorage.hpp"
namespace tracy
{
namespace data { extern AchievementCategory* AchievementCategories[]; }
AchievementsMgr::AchievementsMgr()
{
auto cat = data::AchievementCategories;
while( *cat )
{
FillMap( (*cat)->items, *cat );
cat++;
}
const auto fn = tracy::GetSavePath( "achievements.ini" );
auto ini = ini_load( fn );
if( !ini ) return;
for( auto& v : m_map )
{
uint64_t unlockTime, doneTime;
int hideCompleted, hideNew;
if( ini_sget( ini, v.first, "unlockTime", "%" PRIu64, &unlockTime ) &&
ini_sget( ini, v.first, "doneTime", "%" PRIu64, &doneTime ) &&
ini_sget( ini, v.first, "hideCompleted", "%d", &hideCompleted ) &&
ini_sget( ini, v.first, "hideNew", "%d", &hideNew ) )
{
auto& it = v.second.item;
it->unlockTime = unlockTime;
it->doneTime = doneTime;
it->hideCompleted = hideCompleted != 0;
it->hideNew = hideNew != 0;
}
}
for( auto& v : m_map )
{
auto& it = v.second.item;
if( it->doneTime > 0 )
{
auto c = it->items;
if( c )
{
while( *c )
{
if( (*c)->unlockTime == 0 ) (*c)->unlockTime = it->doneTime;
c++;
}
}
c = it->unlocks;
if( c )
{
while( *c )
{
if( (*c)->unlockTime == 0 ) (*c)->unlockTime = it->doneTime;
c++;
}
}
}
}
for( auto& v : m_map )
{
if( v.second.category->unlockTime == 0 && v.second.item->unlockTime > 0 )
{
v.second.category->unlockTime = v.second.item->unlockTime;
}
}
auto c = data::AchievementCategories;
while( *c )
{
if( (*c)->unlockTime > 0 )
{
auto items = (*c)->items;
while( *items )
{
if( (*items)->unlockTime == 0 ) (*items)->unlockTime = (*c)->unlockTime;
items++;
}
}
c++;
}
ini_free( ini );
}
AchievementsMgr::~AchievementsMgr()
{
Save();
}
void AchievementsMgr::Achieve( const char* id )
{
auto it = m_map.find( id );
assert( it != m_map.end() );
auto& a = *it->second.item;
if( a.unlockTime == 0 ) return;
if( a.doneTime > 0 ) return;
const auto t = uint64_t( time( nullptr ) );
a.doneTime = uint64_t( t );
m_queue.push_back( &a );
auto c = a.items;
if( c )
{
while( *c ) (*c++)->unlockTime = t;
}
c = a.unlocks;
if( c )
{
while( *c )
{
(*c)->unlockTime = t;
auto cit = m_map.find( (*c)->id );
if( cit->second.category->unlockTime == 0 ) cit->second.category->unlockTime = t;
c++;
}
}
Save();
}
data::AchievementCategory** AchievementsMgr::GetCategories() const
{
return data::AchievementCategories;
}
data::AchievementCategory* AchievementsMgr::GetCategoryForAchievement( const char* id ) const
{
auto it = m_map.find( id );
assert( it != m_map.end() );
return it->second.category;
}
data::AchievementItem* AchievementsMgr::GetNextQueue()
{
if( m_queue.empty() ) return nullptr;
return m_queue.front();
}
void AchievementsMgr::PopQueue()
{
assert( !m_queue.empty() );
m_queue.erase( m_queue.begin() );
}
bool AchievementsMgr::NeedsAttention() const
{
for( auto& v : m_map )
{
auto& it = v.second.item;
if( it->unlockTime > 0 && !it->hideNew ) return true;
if( it->doneTime > 0 && !it->hideCompleted ) return true;
}
return false;
}
bool AchievementsMgr::CategoryNeedsAttention( const char* id ) const
{
auto c = data::AchievementCategories;
while( *c )
{
if( strcmp( (*c)->id, id ) == 0 )
{
for( auto& v : m_map )
{
if( v.second.category == (*c) )
{
auto& it = v.second.item;
if( it->unlockTime > 0 && !it->hideNew ) return true;
if( it->doneTime > 0 && !it->hideCompleted ) return true;
}
}
return false;
}
c++;
}
assert( false );
return false;
}
void AchievementsMgr::FillMap( data::AchievementItem** items, data::AchievementCategory* category )
{
while( *items )
{
m_map.emplace( (*items)->id, AchievementPair { *items, category } );
if( (*items)->items) FillMap( (*items)->items, category );
items++;
}
}
void AchievementsMgr::Save()
{
const auto fn = tracy::GetSavePath( "achievements.ini" );
FILE* f = fopen( fn, "wb" );
if( !f ) return;
for( auto& v : m_map )
{
auto& it = v.second.item;
fprintf( f, "[%s]\n", it->id );
fprintf( f, "unlockTime=%" PRIu64 "\n", it->unlockTime );
fprintf( f, "doneTime=%" PRIu64 "\n", it->doneTime );
fprintf( f, "hideCompleted=%d\n", it->hideCompleted ? 1 : 0 );
fprintf( f, "hideNew=%d\n\n", it->hideNew ? 1 : 0 );
}
fclose( f );
}
}

View File

@@ -1,82 +0,0 @@
#ifndef __TRACYACHIEVEMENTS_HPP__
#define __TRACYACHIEVEMENTS_HPP__
#include <stdint.h>
#include <string>
#include <vector>
#include "imgui.h"
#include "TracyCharUtil.hpp"
#include "tracy_robin_hood.h"
namespace tracy
{
namespace data
{
struct ctx
{
ImFont* big;
ImFont* small;
ImFont* fixed;
};
struct AchievementItem
{
const char* id;
const char* name;
void(*description)(const ctx&);
AchievementItem** items;
AchievementItem** unlocks;
bool keepOpen;
uint64_t unlockTime;
uint64_t doneTime;
bool hideCompleted;
bool hideNew;
};
struct AchievementCategory
{
const char* id;
const char* name;
AchievementItem** items;
uint64_t unlockTime;
};
}
class AchievementsMgr
{
struct AchievementPair
{
data::AchievementItem* item;
data::AchievementCategory* category;
};
public:
AchievementsMgr();
~AchievementsMgr();
void Achieve( const char* id );
data::AchievementCategory** GetCategories() const;
data::AchievementCategory* GetCategoryForAchievement( const char* id ) const;
data::AchievementItem* GetNextQueue();
void PopQueue();
bool NeedsAttention() const;
bool CategoryNeedsAttention( const char* id ) const;
private:
void FillMap( data::AchievementItem** items, data::AchievementCategory* category );
void Save();
std::vector<data::AchievementItem*> m_queue;
tracy::unordered_flat_map<const char*, AchievementPair, charutil::Hasher, charutil::Comparator> m_map;
};
}
#endif

View File

@@ -1,124 +0,0 @@
#include <assert.h>
#include "imgui.h"
#include "IconsFontAwesome6.h"
#include "TracyBadVersion.hpp"
#include "TracyImGui.hpp"
#include "TracyWeb.hpp"
namespace tracy
{
namespace detail
{
void BadVersionImpl( BadVersionState& badVer, ImFont* big )
{
assert( badVer.state != BadVersionState::Ok );
switch( badVer.state )
{
case BadVersionState::BadFile:
ImGui::OpenPopup( "Bad file" );
break;
case BadVersionState::ReadError:
ImGui::OpenPopup( "File read error" );
break;
case BadVersionState::UnsupportedVersion:
ImGui::OpenPopup( "Unsupported file version" );
break;
case BadVersionState::LegacyVersion:
ImGui::OpenPopup( "Legacy file version" );
break;
case BadVersionState::LoadFailure:
ImGui::OpenPopup( "Trace load failure" );
break;
default:
assert( false );
break;
}
if( ImGui::BeginPopupModal( "Bad file", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) )
{
ImGui::PushFont( big );
TextCentered( ICON_FA_TRIANGLE_EXCLAMATION );
ImGui::PopFont();
ImGui::Text( "The file you are trying to open is not a Tracy dump." );
ImGui::Separator();
if( ImGui::Button( "Oops" ) )
{
ImGui::CloseCurrentPopup();
badVer.state = BadVersionState::Ok;
}
ImGui::EndPopup();
}
if( ImGui::BeginPopupModal( "File read error", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) )
{
ImGui::PushFont( big );
TextCentered( ICON_FA_TRIANGLE_EXCLAMATION );
ImGui::PopFont();
ImGui::Text( "The file you are trying to open cannot be mapped to memory." );
ImGui::Separator();
if( ImGui::Button( "OK" ) )
{
ImGui::CloseCurrentPopup();
badVer.state = BadVersionState::Ok;
}
ImGui::EndPopup();
}
if( ImGui::BeginPopupModal( "Unsupported file version", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) )
{
ImGui::PushFont( big );
TextCentered( ICON_FA_CLOUD_ARROW_DOWN );
ImGui::PopFont();
ImGui::Text( "The file you are trying to open is unsupported.\nYou should update to Tracy %i.%i.%i or newer and try again.", badVer.version >> 16, ( badVer.version >> 8 ) & 0xFF, badVer.version & 0xFF );
ImGui::Separator();
if( ImGui::Button( ICON_FA_DOWNLOAD " Download update" ) )
{
tracy::OpenWebpage( "https://github.com/wolfpld/tracy/releases" );
ImGui::CloseCurrentPopup();
badVer.state = BadVersionState::Ok;
}
ImGui::SameLine();
if( ImGui::Button( "Maybe later" ) )
{
ImGui::CloseCurrentPopup();
badVer.state = BadVersionState::Ok;
}
ImGui::EndPopup();
}
if( ImGui::BeginPopupModal( "Legacy file version", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) )
{
ImGui::PushFont( big );
TextCentered( ICON_FA_GHOST );
ImGui::PopFont();
ImGui::Text( "You are trying to open a file which was created by legacy version %i.%i.%i.\nUse the update utility from an older version of the profiler to convert the file to a supported version.", badVer.version >> 16, ( badVer.version >> 8 ) & 0xFF, badVer.version & 0xFF );
ImGui::Separator();
if( ImGui::Button( "Maybe I don't need it" ) )
{
ImGui::CloseCurrentPopup();
badVer.state = BadVersionState::Ok;
}
ImGui::EndPopup();
}
if( ImGui::BeginPopupModal( "Trace load failure", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) )
{
ImGui::PushFont( big );
TextCentered( ICON_FA_BOMB );
ImGui::PopFont();
ImGui::TextUnformatted( "The file you are trying to open is corrupted." );
ImGui::Spacing();
ImGui::TextUnformatted( badVer.msg.c_str() );
ImGui::Separator();
if( ImGui::Button( "OK" ) )
{
ImGui::CloseCurrentPopup();
badVer.state = BadVersionState::Ok;
}
ImGui::EndPopup();
}
}
}
}

View File

@@ -1,39 +0,0 @@
#ifndef __TRACYBADVERSION_HPP__
#define __TRACYBADVERSION_HPP__
#include <string>
#include "../public/common/TracyForceInline.hpp"
struct ImFont;
namespace tracy
{
struct BadVersionState
{
enum State
{
Ok,
BadFile,
ReadError,
UnsupportedVersion,
LegacyVersion,
LoadFailure
};
State state = Ok;
int version = 0;
std::string msg;
};
namespace detail
{
void BadVersionImpl( BadVersionState& badVer, ImFont* big );
}
tracy_force_inline void BadVersion( BadVersionState& badVer, ImFont* big ) { if( badVer.state != BadVersionState::Ok ) detail::BadVersionImpl( badVer, big ); }
}
#endif

View File

@@ -1,50 +0,0 @@
#ifndef __TRACYBUZZANIM_HPP__
#define __TRACYBUZZANIM_HPP__
#include <assert.h>
namespace tracy
{
template<typename T>
class BuzzAnim
{
public:
bool Match( const T& comp ) const
{
return active && comp == id;
}
float Time() const
{
assert( active );
return time;
}
void Enable( const T& val, float len )
{
active = true;
time = len;
id = val;
}
bool Update( float dt )
{
if( active )
{
time -= dt;
if( time <= 0 ) active = false;
return true;
}
return false;
}
private:
bool active = false;
float time;
T id;
};
}
#endif

View File

@@ -1,36 +0,0 @@
#include <algorithm>
#include "TracyColor.hpp"
namespace tracy
{
uint32_t GetHsvColor( uint64_t hue, int value )
{
const uint8_t h = ( hue * 11400714819323198485ull ) & 0xFF;
const uint8_t s = 108;
const uint8_t v = std::max( 96, 170 - value * 8 );
const uint8_t reg = h / 43;
const uint8_t rem = ( h - ( reg * 43 ) ) * 6;
const uint8_t p = ( v * ( 255 - s ) ) >> 8;
const uint8_t q = ( v * ( 255 - ( ( s * rem ) >> 8 ) ) ) >> 8;
const uint8_t t = ( v * ( 255 - ( ( s * ( 255 - rem ) ) >> 8 ) ) ) >> 8;
uint8_t r, g, b;
switch( reg )
{
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
default: r = v; g = p; b = q; break;
}
return 0xFF000000 | ( r << 16 ) | ( g << 8 ) | b;
}
}

View File

@@ -1,57 +0,0 @@
#ifndef __TRACYCOLOR_HPP__
#define __TRACYCOLOR_HPP__
#include <algorithm>
#include <stdint.h>
#include "../public/common/TracyForceInline.hpp"
namespace tracy
{
uint32_t GetHsvColor( uint64_t hue, int value );
template<int V = 25>
static tracy_force_inline uint32_t HighlightColor( uint32_t color )
{
return 0xFF000000 |
( std::min<int>( 0xFF, ( ( ( color & 0x00FF0000 ) >> 16 ) + V ) ) << 16 ) |
( std::min<int>( 0xFF, ( ( ( color & 0x0000FF00 ) >> 8 ) + V ) ) << 8 ) |
( std::min<int>( 0xFF, ( ( ( color & 0x000000FF ) ) + V ) ) );
}
static tracy_force_inline uint32_t DarkenColorSlightly( uint32_t color )
{
return 0xFF000000 |
( ( ( ( color & 0x00FF0000 ) >> 16 ) * 4 / 5 ) << 16 ) |
( ( ( ( color & 0x0000FF00 ) >> 8 ) * 4 / 5 ) << 8 ) |
( ( ( ( color & 0x000000FF ) ) * 4 / 5 ) );
}
static tracy_force_inline uint32_t DarkenColor( uint32_t color )
{
return 0xFF000000 |
( ( ( ( color & 0x00FF0000 ) >> 16 ) * 2 / 3 ) << 16 ) |
( ( ( ( color & 0x0000FF00 ) >> 8 ) * 2 / 3 ) << 8 ) |
( ( ( ( color & 0x000000FF ) ) * 2 / 3 ) );
}
static tracy_force_inline uint32_t DarkenColorHalf( uint32_t color )
{
return 0xFF000000 |
( ( ( ( color & 0x00FF0000 ) >> 16 ) / 2 ) << 16 ) |
( ( ( ( color & 0x0000FF00 ) >> 8 ) / 2 ) << 8 ) |
( ( ( ( color & 0x000000FF ) ) / 2 ) );
}
static tracy_force_inline uint32_t DarkenColorMore( uint32_t color )
{
return 0xFF000000 |
( ( ( ( color & 0x00FF0000 ) >> 16 ) * 1 / 4 ) << 16 ) |
( ( ( ( color & 0x0000FF00 ) >> 8 ) * 1 / 4 ) << 8 ) |
( ( ( ( color & 0x000000FF ) ) * 1 / 4 ) );
}
}
#endif

View File

@@ -1,29 +0,0 @@
#ifndef __TRACYCONFIG_HPP__
#define __TRACYCONFIG_HPP__
#include "TracyUtility.hpp"
namespace tracy
{
struct Config
{
bool threadedRendering = true;
bool focusLostLimit = true;
int targetFps = 60;
double horizontalScrollMultiplier = 1.0;
double verticalScrollMultiplier = 1.0;
bool memoryLimit = false;
int memoryLimitPercent = 80;
bool achievements = false;
bool achievementsAsked = false;
int dynamicColors = 1;
bool forceColors = false;
int shortenName = (int)ShortenName::NoSpaceAndNormalize;
bool saveUserScale = false;
float userScale = 1.0f;
};
}
#endif

View File

@@ -1,48 +0,0 @@
#ifndef __TRACYDECAYVALUE_HPP__
#define __TRACYDECAYVALUE_HPP__
#include "../public/common/TracyForceInline.hpp"
namespace tracy
{
template<typename T>
class DecayValue
{
public:
DecayValue( const T& init, bool active = false )
: m_value( init )
, m_active( active )
{
}
tracy_force_inline operator const T& () const { return m_value; }
tracy_force_inline T operator->() const { return m_value; }
tracy_force_inline DecayValue& operator=( const T& value )
{
m_value = value;
m_active = true;
return *this;
}
tracy_force_inline void Decay( const T& value )
{
if( m_active )
{
m_active = false;
}
else
{
m_value = value;
}
}
private:
T m_value;
bool m_active;
};
}
#endif

View File

@@ -1,361 +0,0 @@
#include <assert.h>
#include <stdio.h>
#include <inttypes.h>
#include "TracyEventDebug.hpp"
#include "../public/common/TracyQueue.hpp"
namespace tracy
{
void EventDebug( const QueueItem& ev )
{
static FILE* f = fopen( "eventdebug.txt", "wb" );
switch( ev.hdr.type )
{
case QueueType::ZoneText:
fprintf( f, "ev %i (ZoneText)\n", ev.hdr.idx );
break;
case QueueType::ZoneName:
fprintf( f, "ev %i (ZoneName)\n", ev.hdr.idx );
break;
case QueueType::Message:
fprintf( f, "ev %i (Message)\n", ev.hdr.idx );
break;
case QueueType::MessageColor:
fprintf( f, "ev %i (MessageColor)\n", ev.hdr.idx );
break;
case QueueType::MessageCallstack:
fprintf( f, "ev %i (MessageCallstack)\n", ev.hdr.idx );
break;
case QueueType::MessageColorCallstack:
fprintf( f, "ev %i (MessageColorCallstack)\n", ev.hdr.idx );
break;
case QueueType::MessageAppInfo:
fprintf( f, "ev %i (MessageAppInfo)\n", ev.hdr.idx );
break;
case QueueType::ZoneBeginAllocSrcLoc:
fprintf( f, "ev %i (ZoneBeginAllocSrcLoc)\n", ev.hdr.idx );
break;
case QueueType::ZoneBeginAllocSrcLocCallstack:
fprintf( f, "ev %i (ZoneBeginAllocSrcLocCallstack)\n", ev.hdr.idx );
break;
case QueueType::CallstackSerial:
fprintf( f, "ev %i (CallstackSerial)\n", ev.hdr.idx );
break;
case QueueType::Callstack:
fprintf( f, "ev %i (Callstack)\n", ev.hdr.idx );
break;
case QueueType::CallstackAlloc:
fprintf( f, "ev %i (CallstackAlloc)\n", ev.hdr.idx );
break;
case QueueType::CallstackSample:
fprintf( f, "ev %i (CallstackSample)\n", ev.hdr.idx );
break;
case QueueType::CallstackSampleContextSwitch:
fprintf( f, "ev %i (CallstackSampleContextSwitch)\n", ev.hdr.idx );
break;
case QueueType::FrameImage:
fprintf( f, "ev %i (FrameImage)\n", ev.hdr.idx );
break;
case QueueType::ZoneBegin:
fprintf( f, "ev %i (ZoneBegin)\n", ev.hdr.idx );
fprintf( f, "\ttime = %" PRIi64 "\n", ev.zoneBeginLean.time );
break;
case QueueType::ZoneBeginCallstack:
fprintf( f, "ev %i (ZoneBeginCallstack)\n", ev.hdr.idx );
break;
case QueueType::ZoneEnd:
fprintf( f, "ev %i (ZoneEnd)\n", ev.hdr.idx );
fprintf( f, "\ttime = %" PRIi64 "\n", ev.zoneEnd.time );
break;
case QueueType::LockWait:
fprintf( f, "ev %i (LockWait)\n", ev.hdr.idx );
break;
case QueueType::LockObtain:
fprintf( f, "ev %i (LockObtain)\n", ev.hdr.idx );
break;
case QueueType::LockRelease:
fprintf( f, "ev %i (LockRelease)\n", ev.hdr.idx );
break;
case QueueType::LockSharedWait:
fprintf( f, "ev %i (LockSharedWait)\n", ev.hdr.idx );
break;
case QueueType::LockSharedObtain:
fprintf( f, "ev %i (LockSharedObtain)\n", ev.hdr.idx );
break;
case QueueType::LockSharedRelease:
fprintf( f, "ev %i (LockSharedRelease)\n", ev.hdr.idx );
break;
case QueueType::LockName:
fprintf( f, "ev %i (LockName)\n", ev.hdr.idx );
break;
case QueueType::MemAlloc:
fprintf( f, "ev %i (MemAlloc)\n", ev.hdr.idx );
break;
case QueueType::MemAllocNamed:
fprintf( f, "ev %i (MemAllocNamed)\n", ev.hdr.idx );
break;
case QueueType::MemFree:
fprintf( f, "ev %i (MemFree)\n", ev.hdr.idx );
break;
case QueueType::MemFreeNamed:
fprintf( f, "ev %i (MemFreeNamed)\n", ev.hdr.idx );
break;
case QueueType::MemAllocCallstack:
fprintf( f, "ev %i (MemAllocCallstack)\n", ev.hdr.idx );
break;
case QueueType::MemAllocCallstackNamed:
fprintf( f, "ev %i (MemAllocCallstackNamed)\n", ev.hdr.idx );
break;
case QueueType::MemFreeCallstack:
fprintf( f, "ev %i (MemFreeCallstack)\n", ev.hdr.idx );
break;
case QueueType::MemFreeCallstackNamed:
fprintf( f, "ev %i (MemFreeCallstackNamed)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneBegin:
fprintf( f, "ev %i (GpuZoneBegin)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneBeginCallstack:
fprintf( f, "ev %i (GpuZoneBeginCallstack)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneBeginAllocSrcLoc:
fprintf( f, "ev %i (GpuZoneBeginAllocSrcLoc)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneBeginAllocSrcLocCallstack:
fprintf( f, "ev %i (GpuZoneBeginAllocSrcLocCallstack)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneEnd:
fprintf( f, "ev %i (GpuZoneEnd)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneBeginSerial:
fprintf( f, "ev %i (GpuZoneBeginSerial)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneBeginCallstackSerial:
fprintf( f, "ev %i (GpuZoneBeginCallstackSerial)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneBeginAllocSrcLocSerial:
fprintf( f, "ev %i (GpuZoneBeginAllocSrcLocSerial)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneBeginAllocSrcLocCallstackSerial:
fprintf( f, "ev %i (GpuZoneBeginAllocSrcLocCallstackSerial)\n", ev.hdr.idx );
break;
case QueueType::GpuZoneEndSerial:
fprintf( f, "ev %i (GpuZoneEndSerial)\n", ev.hdr.idx );
break;
case QueueType::PlotDataInt:
fprintf( f, "ev %i (PlotDataInt)\n", ev.hdr.idx );
break;
case QueueType::PlotDataFloat:
fprintf( f, "ev %i (PlotDataFloat)\n", ev.hdr.idx );
break;
case QueueType::PlotDataDouble:
fprintf( f, "ev %i (PlotDataDouble)\n", ev.hdr.idx );
break;
case QueueType::ContextSwitch:
fprintf( f, "ev %i (ContextSwitch)\n", ev.hdr.idx );
fprintf( f, "\ttime = %" PRIi64 "\n", ev.contextSwitch.time );
fprintf( f, "\tthread = %" PRIu32 " -> %" PRIu32 "\n", ev.contextSwitch.oldThread, ev.contextSwitch.newThread );
fprintf( f, "\tcpu = %" PRIu8 ", oldThreadWaitReason = %" PRIu8 ", oldThreadState = %" PRIu8 "\n", ev.contextSwitch.cpu, ev.contextSwitch.oldThreadWaitReason, ev.contextSwitch.oldThreadState);
break;
case QueueType::ThreadWakeup:
fprintf( f, "ev %i (ThreadWakeup)\n", ev.hdr.idx );
break;
case QueueType::GpuTime:
fprintf( f, "ev %i (GpuTime)\n", ev.hdr.idx );
break;
case QueueType::GpuContextName:
fprintf( f, "ev %i (GpuContextName)\n", ev.hdr.idx );
break;
case QueueType::CallstackFrameSize:
fprintf( f, "ev %i (CallstackFrameSize)\n", ev.hdr.idx );
break;
case QueueType::SymbolInformation:
fprintf( f, "ev %i (SymbolInformation)\n", ev.hdr.idx );
break;
case QueueType::FiberEnter:
fprintf( f, "ev %i (FiberEnter)\n", ev.hdr.idx );
fprintf( f, "\ttime = %" PRIi64 "\n", ev.fiberEnter.time );
fprintf( f, "\tfiber = %" PRIu64 "\n", ev.fiberEnter.fiber );
fprintf( f, "\tthread = %" PRIu32 "\n", ev.fiberEnter.thread );
break;
case QueueType::FiberLeave:
fprintf( f, "ev %i (FiberLeave)\n", ev.hdr.idx );
fprintf( f, "\ttime = %" PRIi64 "\n", ev.fiberLeave.time );
fprintf( f, "\tthread = %" PRIu32 "\n", ev.fiberLeave.thread );
break;
case QueueType::Terminate:
fprintf( f, "ev %i (Terminate)\n", ev.hdr.idx );
break;
case QueueType::KeepAlive:
fprintf( f, "ev %i (KeepAlive)\n", ev.hdr.idx );
break;
case QueueType::ThreadContext:
fprintf( f, "ev %i (ThreadContext)\n", ev.hdr.idx );
fprintf( f, "\tthread = %" PRIu32 "\n", ev.threadCtx.thread );
break;
case QueueType::GpuCalibration:
fprintf( f, "ev %i (GpuCalibration)\n", ev.hdr.idx );
break;
case QueueType::GpuTimeSync:
fprintf( f, "ev %i (GpuTimeSync)\n", ev.hdr.idx );
break;
case QueueType::Crash:
fprintf( f, "ev %i (Crash)\n", ev.hdr.idx );
break;
case QueueType::CrashReport:
fprintf( f, "ev %i (CrashReport)\n", ev.hdr.idx );
break;
case QueueType::ZoneValidation:
fprintf( f, "ev %i (ZoneValidation)\n", ev.hdr.idx );
fprintf( f, "\tid = %" PRIu32 "\n", ev.zoneValidation.id );
break;
case QueueType::ZoneColor:
fprintf( f, "ev %i (ZoneColor)\n", ev.hdr.idx );
break;
case QueueType::ZoneValue:
fprintf( f, "ev %i (ZoneValue)\n", ev.hdr.idx );
break;
case QueueType::FrameMarkMsg:
fprintf( f, "ev %i (FrameMarkMsg)\n", ev.hdr.idx );
break;
case QueueType::FrameMarkMsgStart:
fprintf( f, "ev %i (FrameMarkMsgStart)\n", ev.hdr.idx );
break;
case QueueType::FrameMarkMsgEnd:
fprintf( f, "ev %i (FrameMarkMsgEnd)\n", ev.hdr.idx );
break;
case QueueType::SourceLocation:
fprintf( f, "ev %i (SourceLocation)\n", ev.hdr.idx );
break;
case QueueType::LockAnnounce:
fprintf( f, "ev %i (LockAnnounce)\n", ev.hdr.idx );
break;
case QueueType::LockTerminate:
fprintf( f, "ev %i (LockTerminate)\n", ev.hdr.idx );
break;
case QueueType::LockMark:
fprintf( f, "ev %i (LockMark)\n", ev.hdr.idx );
break;
case QueueType::MessageLiteral:
fprintf( f, "ev %i (MessageLiteral)\n", ev.hdr.idx );
break;
case QueueType::MessageLiteralColor:
fprintf( f, "ev %i (MessageLiteralColor)\n", ev.hdr.idx );
break;
case QueueType::MessageLiteralCallstack:
fprintf( f, "ev %i (MessageLiteralCallstack)\n", ev.hdr.idx );
break;
case QueueType::MessageLiteralColorCallstack:
fprintf( f, "ev %i (MessageLiteralColorCallstack)\n", ev.hdr.idx );
break;
case QueueType::GpuNewContext:
fprintf( f, "ev %i (GpuNewContext)\n", ev.hdr.idx );
break;
case QueueType::CallstackFrame:
fprintf( f, "ev %i (CallstackFrame)\n", ev.hdr.idx );
break;
case QueueType::SysTimeReport:
fprintf( f, "ev %i (SysTimeReport)\n", ev.hdr.idx );
fprintf( f, "\ttime = %" PRIi64 "\n", ev.sysTime.time );
fprintf( f, "\tsysTime = %f\n", ev.sysTime.sysTime );
break;
case QueueType::TidToPid:
fprintf( f, "ev %i (TidToPid)\n", ev.hdr.idx );
break;
case QueueType::HwSampleCpuCycle:
fprintf( f, "ev %i (HwSampleCpuCycle)\n", ev.hdr.idx );
break;
case QueueType::HwSampleInstructionRetired:
fprintf( f, "ev %i (HwSampleInstructionRetired)\n", ev.hdr.idx );
break;
case QueueType::HwSampleCacheReference:
fprintf( f, "ev %i (HwSampleCacheReference)\n", ev.hdr.idx );
break;
case QueueType::HwSampleCacheMiss:
fprintf( f, "ev %i (HwSampleCacheMiss)\n", ev.hdr.idx );
break;
case QueueType::HwSampleBranchRetired:
fprintf( f, "ev %i (HwSampleBranchRetired)\n", ev.hdr.idx );
break;
case QueueType::HwSampleBranchMiss:
fprintf( f, "ev %i (HwSampleBranchMiss)\n", ev.hdr.idx );
break;
case QueueType::PlotConfig:
fprintf( f, "ev %i (PlotConfig)\n", ev.hdr.idx );
break;
case QueueType::ParamSetup:
fprintf( f, "ev %i (ParamSetup)\n", ev.hdr.idx );
break;
case QueueType::AckServerQueryNoop:
fprintf( f, "ev %i (AckServerQueryNoop)\n", ev.hdr.idx );
break;
case QueueType::AckSourceCodeNotAvailable:
fprintf( f, "ev %i (AckSourceCodeNotAvailable)\n", ev.hdr.idx );
break;
case QueueType::AckSymbolCodeNotAvailable:
fprintf( f, "ev %i (AckSymbolCodeNotAvailable)\n", ev.hdr.idx );
break;
case QueueType::CpuTopology:
fprintf( f, "ev %i (CpuTopology)\n", ev.hdr.idx );
fprintf( f, "\tpackage = %" PRIu32 "\n", ev.cpuTopology.package );
fprintf( f, "\tcore = %" PRIu32 "\n", ev.cpuTopology.core );
fprintf( f, "\tthread = %" PRIu32 "\n", ev.cpuTopology.thread );
break;
case QueueType::SingleStringData:
fprintf( f, "ev %i (SingleStringData)\n", ev.hdr.idx );
break;
case QueueType::SecondStringData:
fprintf( f, "ev %i (SecondStringData)\n", ev.hdr.idx );
break;
case QueueType::MemNamePayload:
fprintf( f, "ev %i (MemNamePayload)\n", ev.hdr.idx );
break;
case QueueType::StringData:
fprintf( f, "ev %i (StringData)\n", ev.hdr.idx );
break;
case QueueType::ThreadName:
fprintf( f, "ev %i (ThreadName)\n", ev.hdr.idx );
break;
case QueueType::PlotName:
fprintf( f, "ev %i (PlotName)\n", ev.hdr.idx );
break;
case QueueType::SourceLocationPayload:
fprintf( f, "ev %i (SourceLocationPayload)\n", ev.hdr.idx );
break;
case QueueType::CallstackPayload:
fprintf( f, "ev %i (CallstackPayload)\n", ev.hdr.idx );
break;
case QueueType::CallstackAllocPayload:
fprintf( f, "ev %i (CallstackAllocPayload)\n", ev.hdr.idx );
break;
case QueueType::FrameName:
fprintf( f, "ev %i (FrameName)\n", ev.hdr.idx );
break;
case QueueType::FrameImageData:
fprintf( f, "ev %i (FrameImageData)\n", ev.hdr.idx );
break;
case QueueType::ExternalName:
fprintf( f, "ev %i (ExternalName)\n", ev.hdr.idx );
break;
case QueueType::ExternalThreadName:
fprintf( f, "ev %i (ExternalThreadName)\n", ev.hdr.idx );
break;
case QueueType::SymbolCode:
fprintf( f, "ev %i (SymbolCode)\n", ev.hdr.idx );
break;
case QueueType::SourceCode:
fprintf( f, "ev %i (SourceCode)\n", ev.hdr.idx );
break;
case QueueType::FiberName:
fprintf( f, "ev %i (FiberName)\n", ev.hdr.idx );
break;
default:
assert( false );
break;
}
fflush( f );
}
}

View File

@@ -1,10 +0,0 @@
#ifndef __TRACYEVENTDEBUG_HPP__
#define __TRACYEVENTDEBUG_HPP__
namespace tracy
{
struct QueueItem;
void EventDebug( const QueueItem& ev );
}
#endif

View File

@@ -1,126 +0,0 @@
#include "TracyFileselector.hpp"
#ifndef TRACY_NO_FILESELECTOR
# ifdef __EMSCRIPTEN__
# include <emscripten.h>
# else
# include <nfd.h>
# endif
#endif
namespace tracy::Fileselector
{
static bool s_hasFailed = false;
void Init()
{
#if !defined TRACY_NO_FILESELECTOR && !defined __EMSCRIPTEN__
NFD_Init();
#endif
}
void Shutdown()
{
#if !defined TRACY_NO_FILESELECTOR && !defined __EMSCRIPTEN__
NFD_Quit();
#endif
}
bool HasFailed()
{
if( s_hasFailed )
{
s_hasFailed = false;
return true;
}
else
{
return false;
}
}
#ifdef __EMSCRIPTEN__
static std::function<void(const char*)> s_openFileCallback;
extern "C" int nativeOpenFile()
{
s_openFileCallback( "upload.tracy" );
return 0;
}
#endif
static bool OpenFileImpl( const char* ext, const char* desc, const std::function<void(const char*)>& callback )
{
#ifndef TRACY_NO_FILESELECTOR
# ifdef __EMSCRIPTEN__
s_openFileCallback = callback;
EM_ASM( {
var input = document.createElement( 'input' );
input.type = 'file';
input.accept = UTF8ToString( $0 );
input.onchange = (e) => {
var file = e.target.files[0];
var reader = new FileReader();
reader.readAsArrayBuffer( file );
reader.onload = () => {
var buf = reader.result;
var view = new Uint8Array( buf );
FS.createDataFile( '/', 'upload.tracy', view, true, true );
Module.ccall( 'nativeOpenFile', 'number', [], [] );
FS.unlink( '/upload.tracy' );
};
};
input.click();
}, ext );
return true;
# else
nfdu8filteritem_t filter = { desc, ext };
nfdu8char_t* fn;
const auto res = NFD_OpenDialogU8( &fn, &filter, 1, nullptr );
if( res == NFD_OKAY )
{
callback( (const char*)fn );
NFD_FreePathU8( fn );
return true;
}
else
{
return res != NFD_ERROR;
}
# endif
#endif
return false;
}
static bool SaveFileImpl( const char* ext, const char* desc, const std::function<void(const char*)>& callback )
{
#if !defined TRACY_NO_FILESELECTOR && !defined __EMSCRIPTEN__
nfdu8filteritem_t filter = { desc, ext };
nfdu8char_t* fn;
const auto res = NFD_SaveDialogU8( &fn, &filter, 1, nullptr, nullptr );
if( res == NFD_OKAY )
{
callback( (const char*)fn );
NFD_FreePathU8( fn );
return true;
}
else
{
return res != NFD_ERROR;
}
#endif
return false;
}
void OpenFile( const char* ext, const char* desc, const std::function<void(const char*)>& callback )
{
if( !OpenFileImpl( ext, desc, callback ) ) s_hasFailed = true;
}
void SaveFile( const char* ext, const char* desc, const std::function<void(const char*)>& callback )
{
if( !SaveFileImpl( ext, desc, callback ) ) s_hasFailed = true;
}
}

View File

@@ -1,18 +0,0 @@
#ifndef __TRACYFILESELECTOR_HPP__
#define __TRACYFILESELECTOR_HPP__
#include <functional>
namespace tracy::Fileselector
{
void Init();
void Shutdown();
bool HasFailed();
void OpenFile( const char* ext, const char* desc, const std::function<void(const char*)>& callback );
void SaveFile( const char* ext, const char* desc, const std::function<void(const char*)>& callback );
}
#endif

View File

@@ -1,19 +0,0 @@
#include "TracyFilesystem.hpp"
#include "TracyView.hpp"
namespace tracy
{
bool SourceFileValid( const char* fn, uint64_t olderThan, const View& view, const Worker& worker )
{
if( worker.GetSourceFileFromCache( fn ).data != nullptr ) return true;
struct stat buf;
if( stat( view.SourceSubstitution( fn ), &buf ) == 0 && ( buf.st_mode & S_IFREG ) != 0 )
{
if(!view.ValidateSourceAge()) return true;
return (uint64_t)buf.st_mtime < olderThan;
}
return false;
}
}

View File

@@ -1,23 +0,0 @@
#ifndef __TRACYFILESYSTEM_HPP__
#define __TRACYFILESYSTEM_HPP__
#include <stdint.h>
#include <sys/stat.h>
namespace tracy
{
class View;
class Worker;
static inline bool FileExists( const char* fn )
{
struct stat buf;
return stat( fn, &buf ) == 0 && ( buf.st_mode & S_IFREG ) != 0;
}
bool SourceFileValid( const char* fn, uint64_t olderThan, const View& view, const Worker& worker );
}
#endif

View File

@@ -1,130 +0,0 @@
#include <assert.h>
#include <algorithm>
#include "TracyPrint.hpp"
#include "TracyImGui.hpp"
extern ImTextureID zigzagTex;
namespace tracy
{
bool s_wasActive = false;
bool WasActive()
{
if( s_wasActive )
{
s_wasActive = false;
return true;
}
return false;
}
void DrawZigZag( ImDrawList* draw, const ImVec2& wpos, double start, double end, double h, uint32_t color )
{
const auto v = ( end - start ) / ( h * 2 );
draw->AddImage( zigzagTex, wpos + ImVec2( start, -h ), wpos + ImVec2( end, h ), ImVec2( 0, 0 ), ImVec2( v, 1 ), color );
}
void DrawStripedRect( ImDrawList* draw, const ImVec2& wpos, double x0, double y0, double x1, double y1, double sw, uint32_t color, bool fix_stripes_in_screen_space, bool inverted )
{
assert( x1 >= x0 );
assert( y1 >= y0 );
assert( sw > 0 );
const auto ww = ImGui::GetItemRectSize().x;
if( x0 > ww || x1 < 0 ) return;
if( x1 - x0 > ww )
{
x0 = std::max<double>( 0, x0 );
x1 = std::min<double>( ww, x1 );
}
x0 += wpos.x;
x1 += wpos.x;
ImGui::PushClipRect( ImVec2( x0, y0 ), ImVec2( x1, y1 ), true );
const auto rw = x1 - x0;
const auto rh = y1 - y0;
const auto cnt = int( ( rh + rw + sw*2 ) / ( sw*2 ) );
auto v0 = ImVec2( x0, y0 - rw );
if ( fix_stripes_in_screen_space )
{
const auto window_height = double( ImGui::GetWindowHeight() );
const auto flipped_v0y = window_height - v0.y; //we transform into a y-is-up coordinate space to achieve upper-left to lower-right stripes. If we didn't, we would calculate values for lower-left to upper-right
const auto manhattan_distance = x0 + flipped_v0y;
const auto in_multiples_of_2_times_sw = int( manhattan_distance / ( sw*2 ) );
const auto floored_manhatten_distance = double( in_multiples_of_2_times_sw*sw*2 ); //floor in terms of 2 * stripe width
const auto corrected_flipped_v0y = ( floored_manhatten_distance - x0 ); //the corrected (floored) y respects the position of the stripes
v0.y = window_height - corrected_flipped_v0y - double( inverted*sw ); //transform back into y-is-down imgui space
}
for( int i=0; i<cnt; i++ )
{
draw->PathLineTo( v0 + ImVec2( 0, i*sw*2 ) );
draw->PathLineTo( v0 + ImVec2( rw, i*sw*2 + rw ) );
draw->PathLineTo( v0 + ImVec2( rw, i*sw*2 + rw + sw ) );
draw->PathLineTo( v0 + ImVec2( 0, i*sw*2 + sw ) );
draw->PathFillConvex( color );
}
ImGui::PopClipRect();
}
void DrawHistogramMinMaxLabel( ImDrawList* draw, int64_t tmin, int64_t tmax, ImVec2 wpos, float w, float ty )
{
const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
const auto ty15 = round( ty * 1.5f );
const auto mintxt = TimeToString( tmin );
const auto maxtxt = TimeToString( tmax );
const auto maxsz = ImGui::CalcTextSize( maxtxt ).x;
DrawLine( draw, dpos, dpos + ImVec2( 0, ty15 ), 0x66FFFFFF );
DrawLine( draw, dpos + ImVec2( w-1, 0 ), dpos + ImVec2( w-1, ty15 ), 0x66FFFFFF );
draw->AddText( wpos + ImVec2( 0, ty15 ), 0x66FFFFFF, mintxt );
draw->AddText( wpos + ImVec2( w-1-maxsz, ty15 ), 0x66FFFFFF, maxtxt );
char range[64];
sprintf( range, ICON_FA_LEFT_LONG " %s " ICON_FA_RIGHT_LONG, TimeToString( tmax - tmin ) );
const auto rsz = ImGui::CalcTextSize( range ).x;
draw->AddText( wpos + ImVec2( round( (w-1-rsz) * 0.5 ), ty15 ), 0x66FFFFFF, range );
}
void PrintSource( const std::vector<Tokenizer::Line>& lines )
{
for( auto& line: lines )
{
auto ptr = line.begin;
auto it = line.tokens.begin();
while( ptr < line.end )
{
if( it == line.tokens.end() )
{
ImGui::TextUnformatted( ptr, line.end );
ImGui::SameLine( 0, 0 );
break;
}
if( ptr < it->begin )
{
ImGui::TextUnformatted( ptr, it->begin );
ImGui::SameLine( 0, 0 );
}
auto color = SyntaxColors[(int)it->color];
TextColoredUnformatted( color, it->begin, it->end );
ImGui::SameLine( 0, 0 );
ptr = it->end;
++it;
}
ImGui::ItemSize( ImVec2( 0, 0 ), 0 );
}
}
}

View File

@@ -1,278 +0,0 @@
#ifndef __TRACYIMGUI_HPP__
#define __TRACYIMGUI_HPP__
#ifdef _MSC_VER
# pragma warning( disable: 4244 ) // conversion from don't care to whatever, possible loss of data
#endif
#include <math.h>
#include <stdint.h>
#include <vector>
#include "imgui.h"
#include "imgui_internal.h"
#include "../public/common/TracyForceInline.hpp"
#include "IconsFontAwesome6.h"
#include "TracySourceTokenizer.hpp"
#if !IMGUI_DEFINE_MATH_OPERATORS
static inline ImVec2 operator+( const ImVec2& l, const ImVec2& r ) { return ImVec2( l.x + r.x, l.y + r.y ); }
static inline ImVec2 operator-( const ImVec2& l, const ImVec2& r ) { return ImVec2( l.x - r.x, l.y - r.y ); }
#endif
namespace tracy
{
extern bool s_wasActive;
bool WasActive();
void DrawZigZag( ImDrawList* draw, const ImVec2& wpos, double start, double end, double h, uint32_t color );
void DrawStripedRect( ImDrawList* draw, const ImVec2& wpos, double x0, double y0, double x1, double y1, double sw, uint32_t color, bool fix_stripes_in_screen_space, bool inverted );
void DrawHistogramMinMaxLabel( ImDrawList* draw, int64_t tmin, int64_t tmax, ImVec2 wpos, float w, float ty );
void PrintSource( const std::vector<Tokenizer::Line>& lines );
static constexpr const uint32_t SyntaxColors[] = {
0xFFB2B2B2, // default
0xFF51AD72, // comment
0xFF1E5EB7, // preprocessor
0xFFFFA3A3, // string
0xFFFFD1A3, // char literal
0xFF87E8FF, // keyword
0xFFE899CE, // number
0xFFE5E5E5, // punctuation
0xFFBF75C6, // type
0xFFE2AF35, // special
};
static constexpr const uint32_t AsmOpTypeColors[] = {
0xFFE2AF35, // None
0xFF358FE2, // Jump
0xFF358FE2, // Branch
0xFF35E2AF, // Call
0xFF35E2AF, // Ret
0xFF22FFFF, // Privileged
};
static constexpr const uint32_t AsmSyntaxColors[] = {
0xFFFFD1A3, // label
0xFFE5E5E5, // default ('[', '+', '*', ',')
0xFF51AD72, // dword/xmmword 'ptr'
0xFFBF75C6, // register
0xFFE899CE, // literal
};
[[maybe_unused]] static inline float GetScale()
{
return ImGui::GetTextLineHeight() / 15.f;
}
[[maybe_unused]] static inline void ImageCentered( ImTextureID user_texture_id, const ImVec2& size )
{
ImGui::SetCursorPosX( ( ImGui::GetWindowWidth() - size.x ) * 0.5f );
ImGui::Image( user_texture_id, size );
}
[[maybe_unused]] static inline void TextCentered( const char* text )
{
const auto tw = ImGui::CalcTextSize( text ).x;
ImGui::SetCursorPosX( ( ImGui::GetWindowWidth() - tw ) * 0.5f );
ImGui::TextUnformatted( text );
}
[[maybe_unused]] static inline bool ButtonCentered( const char* text )
{
const auto tw = ImGui::CalcTextSize( text ).x + ImGui::GetStyle().FramePadding.x * 2;
ImGui::SetCursorPosX( ( ImGui::GetWindowWidth() - tw ) * 0.5f );
return ImGui::Button( text );
}
[[maybe_unused]] static inline void TextColoredUnformatted( uint32_t col, const char* text, const char* end = nullptr )
{
ImGui::PushStyleColor( ImGuiCol_Text, col );
ImGui::TextUnformatted( text, end );
ImGui::PopStyleColor();
}
[[maybe_unused]] static inline void TextColoredUnformatted( const ImVec4& col, const char* text, const char* end = nullptr )
{
ImGui::PushStyleColor( ImGuiCol_Text, col );
ImGui::TextUnformatted( text, end );
ImGui::PopStyleColor();
}
[[maybe_unused]] static inline void TextDisabledUnformatted( const char* begin, const char* end = nullptr )
{
ImGui::PushStyleColor( ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled] );
ImGui::TextUnformatted( begin, end );
ImGui::PopStyleColor();
}
[[maybe_unused]] static inline void TextFocused( const char* label, const char* value )
{
TextDisabledUnformatted( label );
ImGui::SameLine();
ImGui::TextUnformatted( value );
}
[[maybe_unused]] static inline void DrawWaitingDots( double time )
{
s_wasActive = true;
ImGui::TextUnformatted( "" );
auto draw = ImGui::GetWindowDrawList();
const auto wpos = ImGui::GetWindowPos();
const auto ty = ImGui::GetTextLineHeight();
const auto h = ImGui::GetCursorPosY() - ty * 0.5f;
const auto w = ImGui::GetWindowWidth();
draw->AddCircleFilled( wpos + ImVec2( w * 0.5f - ty, h ), ty * ( 0.15f + 0.2f * ( pow( cos( time * 3.5f + 0.3f ), 16.f ) ) ), 0xFFBBBBBB, 12 );
draw->AddCircleFilled( wpos + ImVec2( w * 0.5f , h ), ty * ( 0.15f + 0.2f * ( pow( cos( time * 3.5f ), 16.f ) ) ), 0xFFBBBBBB, 12 );
draw->AddCircleFilled( wpos + ImVec2( w * 0.5f + ty, h ), ty * ( 0.15f + 0.2f * ( pow( cos( time * 3.5f - 0.3f ), 16.f ) ) ), 0xFFBBBBBB, 12 );
}
[[maybe_unused]] static inline bool SmallCheckbox( const char* label, bool* var )
{
ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
auto ret = ImGui::Checkbox( label, var );
ImGui::PopStyleVar();
return ret;
}
[[maybe_unused]] static inline void SmallColorBox( uint32_t color )
{
ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
ImGui::ColorButton( "c1", ImVec4( (color & 0xFF) / 255.f, ((color>>8) & 0xFF ) / 255.f, ((color>>16) & 0xFF ) / 255.f, 1.f ), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop );
ImGui::PopStyleVar();
}
[[maybe_unused]] static inline bool ButtonDisablable( const char* label, bool disabled )
{
if( disabled )
{
ImGui::BeginDisabled();
ImGui::Button( label );
ImGui::EndDisabled();
return false;
}
else
{
return ImGui::Button( label );
}
}
[[maybe_unused]] static inline bool SmallButtonDisablable( const char* label, bool disabled )
{
if( disabled )
{
ImGui::BeginDisabled();
ImGui::SmallButton( label );
ImGui::EndDisabled();
return false;
}
else
{
return ImGui::SmallButton( label );
}
}
[[maybe_unused]] static inline void DrawTextContrast( ImDrawList* draw, const ImVec2& pos, uint32_t color, const char* text )
{
const auto scale = round( GetScale() );
draw->AddText( pos + ImVec2( scale, scale ), 0xAA000000, text );
draw->AddText( pos, color, text );
}
[[maybe_unused]] static inline void DrawTextSuperContrast( ImDrawList* draw, const ImVec2& pos, uint32_t color, const char* text )
{
const auto scale = GetScale();
const auto s1 = round( scale );
const auto s2 = round( scale * 1.5f );
draw->AddText( pos + ImVec2( 0, s2 ), 0xAA000000, text );
draw->AddText( pos + ImVec2( 0, -s2 ), 0xAA000000, text );
draw->AddText( pos + ImVec2( s2, 0 ), 0xAA000000, text );
draw->AddText( pos + ImVec2( -s2, 0 ), 0xAA000000, text );
draw->AddText( pos + ImVec2( s1, s1 ), 0xAA000000, text );
draw->AddText( pos + ImVec2( -s1, s1 ), 0xAA000000, text );
draw->AddText( pos + ImVec2( -s1, -s1 ), 0xAA000000, text );
draw->AddText( pos + ImVec2( s1, -s1 ), 0xAA000000, text );
draw->AddText( pos, color, text );
}
[[maybe_unused]] static void SetButtonHighlightColor()
{
ImGui::PushStyleColor( ImGuiCol_Button, (ImVec4)ImColor::HSV( 0.35f, 0.6f, 0.6f ) );
ImGui::PushStyleColor( ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV( 0.35f, 0.8f, 0.8f ) );
ImGui::PushStyleColor( ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV( 0.35f, 0.7f, 0.7f ) );
}
[[maybe_unused]] static void ToggleButton( const char* label, bool& toggle )
{
const auto active = toggle;
if( active ) SetButtonHighlightColor();
if( ImGui::Button( label ) ) toggle = !toggle;
if( active ) ImGui::PopStyleColor( 3 );
}
[[maybe_unused]] static void SmallToggleButton( const char* label, bool& toggle )
{
const auto active = toggle;
if( active ) SetButtonHighlightColor();
ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
if( ImGui::Button( label ) ) toggle = !toggle;
ImGui::PopStyleVar( 1 );
if( active ) ImGui::PopStyleColor( 3 );
}
[[maybe_unused]] static bool ClipboardButton( int id = 0 )
{
ImGui::PushStyleColor( ImGuiCol_Border, ImVec4( 0.43f, 0.43f, 0.50f, 0.25f ) );
ImGui::PushStyleColor( ImGuiCol_Button, ImVec4( 0.26f, 0.59f, 0.98f, 0.20f ) );
ImGui::PushStyleColor( ImGuiCol_ButtonHovered, ImVec4( 0.26f, 0.59f, 0.98f, 0.5f ) );
ImGui::PushStyleColor( ImGuiCol_ButtonActive, ImVec4( 0.06f, 0.53f, 0.98f, 0.5f ) );
ImGui::PushStyleColor( ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled] );
ImGui::PushID( id );
const auto res = ImGui::SmallButton( ICON_FA_CLIPBOARD );
ImGui::PopID();
ImGui::PopStyleColor( 5 );
return res;
}
[[maybe_unused]] static tracy_force_inline void DrawLine( ImDrawList* draw, const ImVec2& v1, const ImVec2& v2, uint32_t col, float thickness = 1.0f )
{
const ImVec2 data[2] = { v1, v2 };
draw->AddPolyline( data, 2, col, 0, thickness );
}
[[maybe_unused]] static tracy_force_inline void DrawLine( ImDrawList* draw, const ImVec2& v1, const ImVec2& v2, const ImVec2& v3, uint32_t col, float thickness = 1.0f )
{
const ImVec2 data[3] = { v1, v2, v3 };
draw->AddPolyline( data, 3, col, 0, thickness );
}
[[maybe_unused]] static tracy_force_inline void TooltipIfHovered( const char* text )
{
if( !ImGui::IsItemHovered() ) return;
ImGui::BeginTooltip();
ImGui::TextUnformatted( text );
ImGui::EndTooltip();
}
[[maybe_unused]] void tracy_force_inline DrawHelpMarker( const char* desc )
{
TextDisabledUnformatted( "(?)" );
if( ImGui::IsItemHovered() )
{
const auto ty = ImGui::GetTextLineHeight();
ImGui::BeginTooltip();
ImGui::PushTextWrapPos( 450.0f * ty / 15.f );
ImGui::TextUnformatted( desc );
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
}
#endif

View File

@@ -1,28 +0,0 @@
#ifndef __TRACYLOCKHELPERS_HPP__
#define __TRACYLOCKHELPERS_HPP__
#include <stdint.h>
#include "../public/common/TracyForceInline.hpp"
namespace tracy
{
static tracy_force_inline uint64_t GetThreadBit( uint8_t thread )
{
return uint64_t( 1 ) << thread;
}
static tracy_force_inline bool IsThreadWaiting( uint64_t bitlist, uint64_t threadBit )
{
return ( bitlist & threadBit ) != 0;
}
static tracy_force_inline bool AreOtherWaiting( uint64_t bitlist, uint64_t threadBit )
{
return ( bitlist & ~threadBit ) != 0;
}
}
#endif

File diff suppressed because one or more lines are too long

View File

@@ -1,46 +0,0 @@
#include <stdint.h>
namespace tracy
{
struct AsmDesc
{
uint8_t type;
uint16_t width;
};
struct AsmVar
{
int descNum;
AsmDesc desc[5];
int isaSet;
float tp;
int port, uops, minlat, maxlat;
bool minbound, maxbound;
};
struct AsmOp
{
int id;
int descId;
int numVariants;
const AsmVar*const* variant;
};
struct MicroArchitecture
{
int numOps;
const AsmOp*const* ops;
};
extern const char* MicroArchitectureList[];
extern const char* PortList[];
extern const char* OpsList[];
extern const char* OpDescList[];
extern const char* IsaList[];
extern const MicroArchitecture* const MicroArchitectureData[];
extern int OpsNum;
extern int MicroArchitectureNum;
};

View File

@@ -1,90 +0,0 @@
#include <cmath>
#include "TracyMouse.hpp"
#include "imgui_internal.h"
namespace tracy
{
static constexpr int MouseButtons = IM_ARRAYSIZE( ImGuiContext::IO.MouseDown );
static constexpr float MouseDragThreshold = 2;
struct Mouse
{
bool mouseDown[MouseButtons];
bool mouseClicked[MouseButtons];
bool mouseReleased[MouseButtons];
bool mouseDragging[MouseButtons];
ImVec2 mouseDragDelta[MouseButtons];
bool mousePotentialClickRelease[MouseButtons];
};
static Mouse s_mouse = {};
void MouseFrame()
{
for( int i=0; i<MouseButtons; i++ )
{
s_mouse.mouseDown[i] = ImGui::IsMouseDown( i );
s_mouse.mouseClicked[i] = ImGui::IsMouseClicked( i );
s_mouse.mouseReleased[i] = ImGui::IsMouseReleased( i );
s_mouse.mouseDragging[i] = ImGui::IsMouseDragging( i, 0 );
s_mouse.mouseDragDelta[i] = ImGui::GetMouseDragDelta( i, 0 );
if( s_mouse.mouseDragging[i] )
{
if( s_mouse.mouseClicked[i] || s_mouse.mousePotentialClickRelease[i] )
{
if( std::abs( s_mouse.mouseDragDelta[i].x ) < MouseDragThreshold && std::abs( s_mouse.mouseDragDelta[i].y ) < MouseDragThreshold )
{
s_mouse.mouseDragging[i] = false;
}
else
{
s_mouse.mousePotentialClickRelease[i] = false;
}
}
}
else if( !s_mouse.mouseDown[i] && !s_mouse.mouseReleased[i] )
{
s_mouse.mousePotentialClickRelease[i] = false;
}
}
}
bool IsMouseDown( ImGuiMouseButton button )
{
return s_mouse.mouseDown[button];
}
bool IsMouseClicked( ImGuiMouseButton button )
{
return s_mouse.mouseClicked[button];
}
bool IsMouseDragging( ImGuiMouseButton button )
{
return s_mouse.mouseDragging[button];
}
ImVec2 GetMouseDragDelta( ImGuiMouseButton button )
{
return s_mouse.mouseDragDelta[button];
}
void ConsumeMouseEvents( ImGuiMouseButton button )
{
s_mouse.mouseDown[button] = false;
s_mouse.mouseClicked[button] = false;
s_mouse.mouseDragging[button] = false;
}
bool IsMouseClickReleased( ImGuiMouseButton button )
{
if( s_mouse.mouseReleased[button] && s_mouse.mousePotentialClickRelease[button] ) return true;
if( s_mouse.mouseClicked[button] ) s_mouse.mousePotentialClickRelease[button] = true;
return false;
}
}

View File

@@ -1,21 +0,0 @@
#ifndef __TRACYMOUSE_HPP__
#define __TRACYMOUSE_HPP__
#include "imgui.h"
namespace tracy
{
void MouseFrame();
bool IsMouseDown( ImGuiMouseButton button );
bool IsMouseClicked( ImGuiMouseButton button );
bool IsMouseDragging( ImGuiMouseButton button );
ImVec2 GetMouseDragDelta( ImGuiMouseButton button );
void ConsumeMouseEvents( ImGuiMouseButton button );
bool IsMouseClickReleased( ImGuiMouseButton button );
}
#endif

View File

@@ -1,31 +0,0 @@
#include "TracyFileHeader.hpp"
#include "TracyProtoHistory.hpp"
namespace tracy
{
constexpr ProtocolHistory_t ProtocolHistoryArr[] = {
{ 74, FileVersion( 0, 12, 0 ), FileVersion( 0, 12, 2 ) },
{ 69, FileVersion( 0, 11, 1 ) },
{ 66, FileVersion( 0, 11, 0 ) },
{ 64, FileVersion( 0, 10, 0 ) },
{ 63, FileVersion( 0, 9, 0 ), FileVersion( 0, 9, 1 ) },
{ 57, FileVersion( 0, 8, 2 ) },
{ 56, FileVersion( 0, 8, 1 ) },
{ 55, FileVersion( 0, 8, 0 ) },
{ 46, FileVersion( 0, 7, 6 ), FileVersion( 0, 7, 8 ) },
{ 44, FileVersion( 0, 7, 5 ) },
{ 42, FileVersion( 0, 7, 3 ), FileVersion( 0, 7, 4 ) },
{ 40, FileVersion( 0, 7, 1 ), FileVersion( 0, 7, 2 ) },
{ 35, FileVersion( 0, 7, 0 ) },
{ 25, FileVersion( 0, 6, 2 ), FileVersion( 0, 6, 3 ) },
{ 24, FileVersion( 0, 6, 1 ) },
{ 23, FileVersion( 0, 6, 0 ) },
{ 14, FileVersion( 0, 5, 0 ) },
{ 1, FileVersion( 0, 4, 1 ) },
{}
};
const ProtocolHistory_t* ProtocolHistory = ProtocolHistoryArr;
}

View File

@@ -1,20 +0,0 @@
#ifndef __TRACYPROTOHISTORY_HPP__
#define __TRACYPROTOHISTORY_HPP__
#include <stdint.h>
namespace tracy
{
struct ProtocolHistory_t
{
uint32_t protocol;
uint32_t minVer;
uint32_t maxVer;
};
extern const ProtocolHistory_t* ProtocolHistory;
}
#endif

View File

@@ -1,106 +0,0 @@
#include "TracySourceContents.hpp"
#include "TracyView.hpp"
#include "TracyWorker.hpp"
namespace tracy
{
SourceContents::SourceContents()
: m_file( nullptr )
, m_fileStringIdx( 0 )
, m_data( nullptr )
, m_dataSize( 0 )
, m_dataBuf( nullptr )
, m_dataBufSize( 0 )
{
}
SourceContents::~SourceContents()
{
delete[] m_dataBuf;
}
void SourceContents::Parse( const char* fileName, const Worker& worker, const View& view )
{
if( m_file == fileName ) return;
m_file = fileName;
m_fileStringIdx = worker.FindStringIdx( fileName );
m_lines.clear();
if( fileName )
{
uint32_t sz;
const auto srcCache = worker.GetSourceFileFromCache( fileName );
if( srcCache.data != nullptr )
{
m_data = srcCache.data;
m_dataSize = srcCache.len;
sz = srcCache.len;
}
else
{
FILE* f = fopen( view.SourceSubstitution( fileName ), "rb" );
if( f )
{
fseek( f, 0, SEEK_END );
sz = ftell( f );
fseek( f, 0, SEEK_SET );
if( sz > m_dataBufSize )
{
delete[] m_dataBuf;
m_dataBuf = new char[sz];
m_dataBufSize = sz;
}
fread( m_dataBuf, 1, sz, f );
m_data = m_dataBuf;
m_dataSize = sz;
fclose( f );
}
else
{
m_file = nullptr;
}
}
if( m_file ) Tokenize( m_data, sz );
}
}
void SourceContents::Parse( const char* source )
{
if( source == m_data ) return;
const size_t len = strlen( source );
m_file = nullptr;
m_fileStringIdx = 0;
m_data = source;
m_dataSize = len;
Tokenize( source, len );
}
void SourceContents::Tokenize( const char* txt, size_t sz )
{
Tokenizer tokenizer;
for(;;)
{
auto end = txt;
while( *end != '\n' && *end != '\r' && end - m_data < sz ) end++;
m_lines.emplace_back( Tokenizer::Line { txt, end, tokenizer.Tokenize( txt, end ) } );
if( end - m_data == sz ) break;
if( *end == '\n' )
{
end++;
if( end - m_data < sz && *end == '\r' ) end++;
}
else if( *end == '\r' )
{
end++;
if( end - m_data < sz && *end == '\n' ) end++;
}
if( end - m_data == sz ) break;
txt = end;
}
}
}

View File

@@ -1,51 +0,0 @@
#ifndef __TRACYSOURCECONTENTS_HPP__
#define __TRACYSOURCECONTENTS_HPP__
#include <stdint.h>
#include <stddef.h>
#include <vector>
#include "TracySourceTokenizer.hpp"
namespace tracy
{
class View;
class Worker;
class SourceContents
{
public:
SourceContents();
~SourceContents();
void Parse( const char* fileName, const Worker& worker, const View& view );
void Parse( const char* source );
const std::vector<Tokenizer::Line>& get() const { return m_lines; }
bool empty() const { return m_lines.empty(); }
const char* filename() const { return m_file; }
uint32_t idx() const { return m_fileStringIdx; }
bool is_cached() const { return m_data != m_dataBuf; }
const char* data() const { return m_data; }
size_t data_size() const { return m_dataSize; }
private:
void Tokenize( const char* txt, size_t sz );
const char* m_file;
uint32_t m_fileStringIdx;
const char* m_data;
size_t m_dataSize;
char* m_dataBuf;
size_t m_dataBufSize;
std::vector<Tokenizer::Line> m_lines;
};
}
#endif

View File

@@ -1,421 +0,0 @@
#include "tracy_robin_hood.h"
#include "TracyCharUtil.hpp"
#include "TracySourceTokenizer.hpp"
namespace tracy
{
namespace {
static unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> GetKeywords()
{
unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> ret;
for( auto& v : {
"alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept",
"bitand", "bitor", "break", "case", "catch", "class", "compl", "concept", "const", "consteval",
"constexpr", "constinit", "const_cast", "continue", "co_await", "co_return", "co_yield", "decltype",
"default", "delete", "do", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "for",
"friend", "if", "inline", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "operator",
"or", "or_eq", "private", "protected", "public", "reflexpr", "register", "reinterpret_cast",
"return", "requires", "sizeof", "static", "static_assert", "static_cast", "struct", "switch",
"synchronized", "template", "thread_local", "throw", "try", "typedef", "typeid", "typename",
"union", "using", "virtual", "volatile", "while", "xor", "xor_eq", "override", "final", "import",
"module", "transaction_safe", "transaction_safe_dynamic" } )
{
ret.insert( v );
}
return ret;
}
static unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> GetTypes()
{
unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> ret;
for( auto& v : {
"bool", "char", "char8_t", "char16_t", "char32_t", "double", "float", "int", "long", "short", "signed",
"unsigned", "void", "wchar_t", "size_t", "int8_t", "int16_t", "int32_t", "int64_t", "int_fast8_t",
"int_fast16_t", "int_fast32_t", "int_fast64_t", "int_least8_t", "int_least16_t", "int_least32_t",
"int_least64_t", "intmax_t", "intptr_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t", "uint_fast8_t",
"uint_fast16_t", "uint_fast32_t", "uint_fast64_t", "uint_least8_t", "uint_least16_t", "uint_least32_t",
"uint_least64_t", "uintmax_t", "uintptr_t", "type_info", "bad_typeid", "bad_cast", "type_index",
"clock_t", "time_t", "tm", "timespec", "ptrdiff_t", "nullptr_t", "max_align_t", "auto",
"__m64", "__m128", "__m128i", "__m128d", "__m256", "__m256i", "__m256d", "__m512", "__m512i",
"__m512d", "__mmask8", "__mmask16", "__mmask32", "__mmask64",
"int8x8_t", "int16x4_t", "int32x2_t", "int64x1_t", "uint8x8_t", "uint16x4_t", "uint32x2_t",
"uint64x1_t", "float32x2_t", "poly8x8_t", "poly16x4_t", "int8x16_t", "int16x8_t", "int32x4_t",
"int64x2_t", "uint8x16_t", "uint16x8_t", "uint32x4_t", "uint64x2_t", "float32x4_t", "poly8x16_t",
"poly16x8_t",
"int8x8x2_t", "int16x4x2_t", "int32x2x2_t", "int64x1x2_t", "uint8x8x2_t", "uint16x4x2_t",
"uint32x2x2_t", "uint64x1x2_t", "float32x2x2_t", "poly8x8x2_t", "poly16x4x2_t", "int8x16x2_t",
"int16x8x2_t", "int32x4x2_t", "int64x2x2_t", "uint8x16x2_t", "uint16x8x2_t", "uint32x4x2_t",
"uint64x2x2_t", "float32x4x2_t", "poly8x16x2_t", "poly16x8x2_t",
"int8x8x3_t", "int16x4x3_t", "int32x2x3_t", "int64x1x3_t", "uint8x8x3_t", "uint16x4x3_t",
"uint32x2x3_t", "uint64x1x3_t", "float32x2x3_t", "poly8x8x3_t", "poly16x4x3_t", "int8x16x3_t",
"int16x8x3_t", "int32x4x3_t", "int64x2x3_t", "uint8x16x3_t", "uint16x8x3_t", "uint32x4x3_t",
"uint64x2x3_t", "float32x4x3_t", "poly8x16x3_t", "poly16x8x3_t",
"int8x8x4_t", "int16x4x4_t", "int32x2x4_t", "int64x1x4_t", "uint8x8x4_t", "uint16x4x4_t",
"uint32x2x4_t", "uint64x1x4_t", "float32x2x4_t", "poly8x8x4_t", "poly16x4x4_t", "int8x16x4_t",
"int16x8x4_t", "int32x4x4_t", "int64x2x4_t", "uint8x16x4_t", "uint16x8x4_t", "uint32x4x4_t",
"uint64x2x4_t", "float32x4x4_t", "poly8x16x4_t", "poly16x8x4_t" } )
{
ret.insert( v );
}
return ret;
}
static unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> GetSpecial()
{
unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> ret;
for( auto& v : { "this", "nullptr", "true", "false", "goto", "NULL" } )
{
ret.insert( v );
}
return ret;
}
static unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> GetAsmRegs()
{
unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> ret;
for( auto& v : {
// x64
"ah", "al", "ax", "bh", "bl", "bp", "bpl", "bx", "ch", "cl", "cs", "cx", "dh", "di", "dil", "dl", "ds", "dx",
"eax", "ebp", "ebx", "ecx", "edi", "edx", "flags", "eip", "eiz", "es", "esi", "esp", "fpsw", "fs", "gs", "ip",
"rax", "rbp", "rbx", "rcx", "rdi", "rdx", "rip", "riz", "rsi", "rsp", "si", "sil", "sp", "spl", "ss", "cr0",
"cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15",
"dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7", "dr8", "dr9", "dr10", "dr11", "dr12", "dr13", "dr14",
"dr15", "fp0", "fp1", "fp2", "fp3", "fp4", "fp5", "fp6", "fp7", "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7",
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"st(0)", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4",
"xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", "xmm16", "xmm17",
"xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23", "xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29",
"xmm30", "xmm31", "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", "ymm8", "ymm9", "ymm10",
"ymm11", "ymm12", "ymm13", "ymm14", "ymm15", "ymm16", "ymm17", "ymm18", "ymm19", "ymm20", "ymm21", "ymm22",
"ymm23", "ymm24", "ymm25", "ymm26", "ymm27", "ymm28", "ymm29", "ymm30", "ymm31", "zmm0", "zmm1", "zmm2",
"zmm3", "zmm4", "zmm5", "zmm6", "zmm7", "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15",
"zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", "zmm23", "zmm24", "zmm25", "zmm26", "zmm27",
"zmm28", "zmm29", "zmm30", "zmm31", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b", "r8d", "r9d",
"r10d", "r11d", "r12d", "r13d", "r14d", "r15d", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w",
// ARM
"apsr", "apsr_nzcv", "cpsr", "fpexc", "fpinst", "fpscr", "fpscr_nzcv", "fpsid", "itstate", "lr", "pc", "sp",
"spsr", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
"fpinst2", "mvfr0", "mvfr1", "mvfr2", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11",
"q12", "q13", "q14", "q15", "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12",
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", "s16",
"s17", "s18", "s19", "s20", "s21", "s22", "s23", "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", "w0",
"w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", "w16", "w17",
"w18", "w19", "w20", "w21", "w22", "w23", "w24", "w25", "w26", "w27", "w28", "w29", "w30", "x0", "x1", "x2",
"x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19",
"x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "v0", "v1", "v2", "v3", "v4",
"v5", "v6", "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", "v20",
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30", "xzr", "wzr", "b0", "b1", "b2", "b3",
"b4", "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12", "b13", "b14", "b15", "b16", "b17", "b18", "b19",
"b20", "b21", "b22", "b23", "b24", "b25", "b26", "b27", "b28", "b29", "b30", "h0", "h1", "h2", "h3", "h4",
"h5", "h6", "h7", "h8", "h9", "h10", "h11", "h12", "h13", "h14", "h15", "h16", "h17", "h18", "h19", "h20",
"h21", "h22", "h23", "h24", "h25", "h26", "h27", "h28", "h29", "h30" } )
{
ret.insert( v );
}
return ret;
}
static unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> GetAsmSizeDirectives()
{
unordered_flat_set<const char*, charutil::Hasher, charutil::Comparator> ret;
for( auto& v : { "byte", "word", "dword", "qword", "xmmword", "ymmword", "zmmword" } )
{
ret.insert( v );
}
return ret;
}
}
Tokenizer::Tokenizer()
: m_isInComment( false )
, m_isInPreprocessor( false )
{
}
std::vector<Tokenizer::Token> Tokenizer::Tokenize( const char* begin, const char* end )
{
std::vector<Token> ret;
if( m_isInPreprocessor )
{
if( begin == end )
{
m_isInPreprocessor = false;
return ret;
}
if( *(end-1) != '\\' ) m_isInPreprocessor = false;
ret.emplace_back( Token { begin, end, TokenColor::Preprocessor } );
return ret;
}
const bool first = !m_isInComment;
while( begin != end )
{
if( m_isInComment )
{
const auto pos = begin;
for(;;)
{
while( begin != end && *begin != '*' ) begin++;
begin++;
if( begin < end )
{
if( *begin == '/' )
{
begin++;
ret.emplace_back( Token { pos, begin, TokenColor::Comment } );
m_isInComment = false;
break;
}
}
else
{
ret.emplace_back( Token { pos, end, TokenColor::Comment } );
return ret;
}
}
}
else
{
while( begin != end && isspace( (uint8_t)*begin ) ) begin++;
if( first && begin < end && *begin == '#' )
{
if( *(end-1) == '\\' ) m_isInPreprocessor = true;
ret.emplace_back( Token { begin, end, TokenColor::Preprocessor } );
return ret;
}
const auto pos = begin;
const auto col = IdentifyToken( begin, end );
ret.emplace_back( Token { pos, begin, col } );
}
}
return ret;
}
static bool TokenizeNumber( const char*& begin, const char* end )
{
const bool startNum = *begin >= '0' && *begin <= '9';
if( *begin != '+' && *begin != '-' && !startNum ) return false;
begin++;
bool hasNum = startNum;
while( begin < end && ( ( *begin >= '0' && *begin <= '9' ) || *begin == '\'' ) )
{
hasNum = true;
begin++;
}
if( !hasNum ) return false;
bool isFloat = false, isBinary = false;
if( begin < end )
{
if( *begin == '.' )
{
isFloat = true;
begin++;
while( begin < end && ( ( *begin >= '0' && *begin <= '9' ) || *begin == '\'' ) ) begin++;
}
else if( *begin == 'x' || *begin == 'X' )
{
// hexadecimal
begin++;
while( begin < end && ( ( *begin >= '0' && *begin <= '9' ) || ( *begin >= 'a' && *begin <= 'f' ) || ( *begin >= 'A' && *begin <= 'F' ) || *begin == '\'' ) ) begin++;
}
else if( *begin == 'b' || *begin == 'B' )
{
isBinary = true;
begin++;
while( begin < end && ( ( *begin == '0' || *begin == '1' ) || *begin == '\'' ) ) begin++;
}
}
if( !isBinary )
{
if( begin < end && ( *begin == 'e' || *begin == 'E' || *begin == 'p' || *begin == 'P' ) )
{
isFloat = true;
begin++;
if( begin < end && ( *begin == '+' || *begin == '-' ) ) begin++;
bool hasDigits = false;
while( begin < end && ( ( *begin >= '0' && *begin <= '9' ) || ( *begin >= 'a' && *begin <= 'f' ) || ( *begin >= 'A' && *begin <= 'F' ) || *begin == '\'' ) )
{
hasDigits = true;
begin++;
}
if( !hasDigits ) return false;
}
if( begin < end && ( *begin == 'f' || *begin == 'F' || *begin == 'l' || *begin == 'L' ) ) begin++;
}
if( !isFloat )
{
while( begin < end && ( *begin == 'u' || *begin == 'U' || *begin == 'l' || *begin == 'L' ) ) begin++;
}
return true;
}
Tokenizer::TokenColor Tokenizer::IdentifyToken( const char*& begin, const char* end )
{
static const auto s_keywords = GetKeywords();
static const auto s_types = GetTypes();
static const auto s_special = GetSpecial();
if( *begin == '"' )
{
begin++;
while( begin < end )
{
if( *begin == '"' )
{
begin++;
break;
}
begin += 1 + ( *begin == '\\' && end - begin > 1 && *(begin+1) == '"' );
}
return TokenColor::String;
}
if( *begin == '\'' )
{
begin++;
if( begin < end && *begin == '\\' ) begin++;
if( begin < end ) begin++;
if( begin < end && *begin == '\'' ) begin++;
return TokenColor::CharacterLiteral;
}
if( ( *begin >= 'a' && *begin <= 'z' ) || ( *begin >= 'A' && *begin <= 'Z' ) || *begin == '_' )
{
const char* tmp = begin;
begin++;
while( begin < end && ( ( *begin >= 'a' && *begin <= 'z' ) || ( *begin >= 'A' && *begin <= 'Z' ) || ( *begin >= '0' && *begin <= '9' ) || *begin == '_' ) ) begin++;
if( begin - tmp <= 24 )
{
char buf[25];
memcpy( buf, tmp, begin-tmp );
buf[begin-tmp] = '\0';
if( s_keywords.find( buf ) != s_keywords.end() ) return TokenColor::Keyword;
if( s_types.find( buf ) != s_types.end() ) return TokenColor::Type;
if( s_special.find( buf ) != s_special.end() ) return TokenColor::Special;
}
return TokenColor::Default;
}
const char* tmp = begin;
if( TokenizeNumber( begin, end ) ) return TokenColor::Number;
begin = tmp;
if( *begin == '/' && end - begin > 1 )
{
if( *(begin+1) == '/' )
{
begin = end;
return TokenColor::Comment;
}
if( *(begin+1) == '*' )
{
begin += 2;
for(;;)
{
while( begin < end && *begin != '*' ) begin++;
if( begin == end )
{
m_isInComment = true;
return TokenColor::Comment;
}
begin++;
if( begin < end && *begin == '/' )
{
begin++;
return TokenColor::Comment;
}
}
}
}
while( begin < end )
{
switch( *begin )
{
case '[':
case ']':
case '{':
case '}':
case '!':
case '%':
case '^':
case '&':
case '*':
case '(':
case ')':
case '-':
case '+':
case '=':
case '~':
case '|':
case '<':
case '>':
case '?':
case ':':
case '/':
case ';':
case ',':
case '.':
begin++;
break;
default:
goto out;
}
}
out:
if( begin != tmp ) return TokenColor::Punctuation;
begin = end;
return TokenColor::Default;
}
std::vector<Tokenizer::AsmToken> Tokenizer::TokenizeAsm( const char* begin, const char* end )
{
std::vector<AsmToken> ret;
while( begin != end )
{
while( begin != end && isspace( (uint8_t)*begin ) ) begin++;
const auto pos = begin;
const auto col = IdentifyAsmToken( begin, end );
ret.emplace_back( AsmToken { pos, begin, col } );
}
return ret;
}
Tokenizer::AsmTokenColor Tokenizer::IdentifyAsmToken( const char*& begin, const char* end )
{
static const auto s_regs = GetAsmRegs();
static const auto s_sizes = GetAsmSizeDirectives();
while( begin < end && *begin == ' ' ) begin++;
if( ( *begin >= 'a' && *begin <= 'z' ) || ( *begin >= 'A' && *begin <= 'Z' ) )
{
const char* tmp = begin;
begin++;
while( begin < end && ( ( *begin >= 'a' && *begin <= 'z' ) || ( *begin >= 'A' && *begin <= 'Z' ) || ( *begin >= '0' && *begin <= '9' ) || *begin == '_' ) ) begin++;
if( begin - tmp <= 10 )
{
char buf[11];
memcpy( buf, tmp, begin-tmp );
buf[begin-tmp] = '\0';
if( s_regs.find( buf ) != s_regs.end() ) return AsmTokenColor::Register;
if( s_sizes.find( buf ) != s_sizes.end() )
{
if( end - begin >= 4 && memcmp( begin, " ptr", 4 ) == 0 )
{
begin += 4;
return AsmTokenColor::SizeDirective;
}
}
}
return AsmTokenColor::Default;
}
else if( *begin >= '0' && *begin <= '9' )
{
while( begin < end && ( ( *begin >= 'a' && *begin <= 'z' ) || ( *begin >= 'A' && *begin <= 'Z' ) || ( *begin >= '0' && *begin <= '9' ) || *begin == '_' ) ) begin++;
return AsmTokenColor::Literal;
}
else
{
while( begin < end && !( ( *begin >= 'a' && *begin <= 'z' ) || ( *begin >= 'A' && *begin <= 'Z' ) || ( *begin >= '0' && *begin <= '9' ) || *begin == '_' ) ) begin++;
return AsmTokenColor::Default;
}
}
}

View File

@@ -1,72 +0,0 @@
#ifndef __TRACYSOURCETOKENIZER_HPP__
#define __TRACYSOURCETOKENIZER_HPP__
#include <stdint.h>
#include <vector>
namespace tracy
{
class Tokenizer
{
public:
enum class TokenColor : uint8_t
{
Default,
Comment,
Preprocessor,
String,
CharacterLiteral,
Keyword,
Number,
Punctuation,
Type,
Special
};
struct Token
{
const char* begin;
const char* end;
TokenColor color;
};
struct Line
{
const char* begin;
const char* end;
std::vector<Token> tokens;
};
enum class AsmTokenColor : uint8_t
{
Label, // no-op, padding
Default, // '+', '[', '*', etc
SizeDirective, // byte, word, dword, etc
Register, // rax, rip, etc
Literal, // 0x04, etc
};
struct AsmToken
{
const char* begin;
const char* end;
AsmTokenColor color;
};
Tokenizer();
std::vector<Token> Tokenize( const char* begin, const char* end );
std::vector<AsmToken> TokenizeAsm( const char* begin, const char* end );
private:
TokenColor IdentifyToken( const char*& begin, const char* end );
AsmTokenColor IdentifyAsmToken( const char*& begin, const char* end );
bool m_isInComment;
bool m_isInPreprocessor;
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,305 +0,0 @@
#ifndef __TRACYSOURCEVIEW_HPP__
#define __TRACYSOURCEVIEW_HPP__
#include <limits>
#include <string>
#include <vector>
#include "tracy_robin_hood.h"
#include "TracyCharUtil.hpp"
#include "TracyDecayValue.hpp"
#include "TracySourceContents.hpp"
#include "TracySourceTokenizer.hpp"
#include "../public/common/TracyForceInline.hpp"
#include "../public/common/TracyProtocol.hpp"
struct ImFont;
struct ImVec2;
namespace tracy
{
class View;
class Worker;
struct CallstackFrameData;
class SourceView
{
public:
enum class RegsX86 : uint8_t
{
invalid, flags,
rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8, r9, r10, r11, r12, r13, r14, r15,
mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,
xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9,
xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, xmm16, xmm17, xmm18, xmm19,
xmm20, xmm21, xmm22, xmm23, xmm24, xmm25, xmm26, xmm27, xmm28, xmm29,
xmm30, xmm31, k0, k1, k2, k3, k4, k5, k6, k7,
NUMBER_OF_ENTRIES
};
enum class CostType
{
SampleCount,
Cycles,
SlowBranches,
SlowCache,
Retirements,
BranchesTaken,
BranchMiss,
CacheAccess,
CacheMiss
};
private:
struct AsmOpParams
{
uint8_t type;
uint16_t width;
};
enum class LeaData : uint8_t
{
none,
b,
bd,
bi,
bid,
d,
i,
id,
r,
rd
};
static constexpr int ReadBit = 0x100;
static constexpr int WriteBit = 0x200;
static constexpr int ReuseBit = 0x400;
static constexpr int RegMask = 0x0FF;
static constexpr int FlagMask = 0xF00;
enum class OpType : uint8_t
{
None,
Jump,
Branch,
Call,
Ret,
Privileged
};
struct AsmLine
{
uint64_t addr;
uint64_t jumpAddr;
std::string mnemonic;
std::string operands;
uint8_t len;
LeaData leaData;
OpType opType;
bool jumpConditional;
std::vector<AsmOpParams> params;
std::vector<Tokenizer::AsmToken> opTokens;
union
{
RegsX86 readX86[12];
};
union
{
RegsX86 writeX86[20];
};
uint16_t regData[20];
};
enum { AsmLineSize = sizeof( AsmLine ) };
struct JumpData
{
uint64_t min;
uint64_t max;
size_t level;
std::vector<uint64_t> source;
};
enum
{
DisplaySource,
DisplayAsm,
DisplayMixed
};
struct AddrStat
{
uint64_t local;
uint64_t ext;
AddrStat& operator+=( const AddrStat& other )
{
local += other.local;
ext += other.ext;
return *this;
}
};
struct AddrStatData
{
AddrStat ipTotalSrc = {};
AddrStat ipTotalAsm = {};
AddrStat ipMaxSrc = {};
AddrStat ipMaxAsm = {};
AddrStat hwMaxSrc = {};
AddrStat hwMaxAsm = {};
unordered_flat_map<uint64_t, AddrStat> ipCountSrc, ipCountAsm;
unordered_flat_map<uint64_t, AddrStat> hwCountSrc, hwCountAsm;
};
struct History
{
const char* fileName;
int64_t line;
uint64_t baseAddr;
uint64_t symAddr;
};
public:
SourceView();
void UpdateFont( ImFont* fixed, ImFont* small, ImFont* big ) { m_font = fixed; m_smallFont = small; m_bigFont = big; }
void SetCpuId( uint32_t cpuid );
void OpenSource( const char* fileName, int line, const View& view, const Worker& worker );
void OpenSymbol( const char* fileName, int line, uint64_t baseAddr, uint64_t symAddr, Worker& worker, const View& view, bool updateHistory = true );
void Render( Worker& worker, View& view );
void CalcInlineStats( bool val ) { m_calcInlineStats = val; }
bool IsSymbolView() const { return !m_asm.empty(); }
private:
void ParseSource( const char* fileName, const Worker& worker, const View& view );
bool Disassemble( uint64_t symAddr, const Worker& worker );
void SelectViewMode();
void RenderSimpleSourceView();
void RenderSymbolView( Worker& worker, View& view );
void RenderSymbolSourceView( const AddrStatData& as, Worker& worker, const View& view, bool hasInlines );
uint64_t RenderSymbolAsmView( const AddrStatData& as, Worker& worker, View& view );
void RenderLine( const Tokenizer::Line& line, int lineNum, const AddrStat& ipcnt, const AddrStatData& as, Worker* worker, const View* view );
void RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const AddrStatData& as, Worker& worker, uint64_t& jumpOut, int maxAddrLen, int maxAddrLenRel, View& view );
void RenderHwLinePart( size_t cycles, size_t retired, size_t branchRetired, size_t branchMiss, size_t cacheRef, size_t cacheMiss, size_t branchRel, size_t branchRelMax, size_t cacheRel, size_t cacheRelMax, const ImVec2& ts );
void SelectLine( uint32_t line, const Worker* worker, bool updateAsmLine = true, uint64_t targetAddr = 0, bool changeAsmLine = true );
void SelectAsmLines( uint32_t file, uint32_t line, const Worker& worker, bool updateAsmLine = true, uint64_t targetAddr = 0, bool changeAsmLine = true );
void SelectAsmLinesHover( uint32_t file, uint32_t line, const Worker& worker );
void GatherIpHwStats( AddrStatData& as, Worker& worker, const View& view, CostType cost );
void GatherIpStats( uint64_t baseAddr, AddrStatData& as, const Worker& worker, bool limitView, const View& view );
void GatherAdditionalIpStats( uint64_t baseAddr, AddrStatData& as, const Worker& worker, bool limitView, const View& view );
void GatherChildStats( uint64_t baseAddr, unordered_flat_map<uint64_t, uint32_t>& vec, Worker& worker, bool limitView, const View& view );
uint32_t CountAsmIpStats( uint64_t baseAddr, const Worker& worker, bool limitView, const View& view );
void CountHwStats( AddrStatData& as, Worker& worker, const View& view );
void SelectMicroArchitecture( const char* moniker );
void ResetAsm();
void FollowRead( size_t line, RegsX86 reg, size_t limit );
void FollowWrite( size_t line, RegsX86 reg, size_t limit );
void CheckRead( size_t line, RegsX86 reg, size_t limit );
void CheckWrite( size_t line, RegsX86 reg, size_t limit );
bool IsInContext( const Worker& worker, uint64_t addr ) const;
const std::vector<uint64_t>* GetAddressesForLocation( uint32_t fileStringIdx, uint32_t line, const Worker& worker );
tracy_force_inline float CalcJumpSeparation( float scale );
#ifndef TRACY_NO_FILESELECTOR
void Save( const Worker& worker, size_t start = 0, size_t stop = std::numeric_limits<size_t>::max() );
#endif
tracy_force_inline void SetFont();
tracy_force_inline void UnsetFont();
ImFont* m_font;
ImFont* m_smallFont;
ImFont* m_bigFont;
uint64_t m_symAddr;
uint64_t m_baseAddr;
uint64_t m_targetAddr;
int m_targetLine;
int m_selectedLine;
int m_asmSelected;
DecayValue<int> m_hoveredLine;
DecayValue<uint32_t> m_hoveredSource;
int m_displayMode;
uint32_t m_codeLen;
int32_t m_disasmFail;
DecayValue<uint64_t> m_highlightAddr;
int m_asmCountBase;
bool m_asmRelative;
bool m_asmBytes;
bool m_asmShowSourceLocation;
bool m_calcInlineStats;
uint8_t m_maxAsmBytes;
uint64_t m_jumpPopupAddr;
const CallstackFrameData* m_localCallstackPopup;
bool m_hwSamples, m_hwSamplesRelative;
bool m_childCalls;
bool m_childCallList;
bool m_propagateInlines;
CostType m_cost;
SourceContents m_source;
SourceContents m_sourceTooltip;
std::vector<AsmLine> m_asm;
unordered_flat_map<uint64_t, uint32_t> m_locMap;
unordered_flat_map<uint64_t, JumpData> m_jumpTable;
unordered_flat_set<uint64_t> m_jumpOut;
size_t m_maxJumpLevel;
bool m_showJumps;
unordered_flat_map<uint64_t, std::vector<uint64_t>> m_locationAddress;
bool m_locAddrIsProp;
unordered_flat_map<uint32_t, uint32_t> m_sourceFiles;
unordered_flat_set<uint64_t> m_selectedAddresses;
unordered_flat_set<uint64_t> m_selectedAddressesHover;
uint32_t m_maxLine;
int m_maxMnemonicLen;
int m_maxOperandLen;
unordered_flat_map<const char*, int, charutil::Hasher, charutil::Comparator> m_microArchOpMap;
CpuArchitecture m_cpuArch;
int m_selMicroArch;
int m_idxMicroArch, m_profileMicroArch;
unordered_flat_set<uint32_t> m_asmSampleSelect;
unordered_flat_set<uint32_t> m_srcSampleSelect;
int32_t m_asmGroupSelect = -1;
int32_t m_srcGroupSelect = -1;
float m_srcWidth;
float m_asmWidth;
float m_jumpOffset;
Tokenizer m_tokenizer;
struct
{
uint32_t file = 0;
uint32_t line = 0;
size_t sel;
std::vector<uint64_t> target;
} m_asmTarget;
std::vector<History> m_history;
size_t m_historyCursor = 0;
};
}
#endif

View File

@@ -1,211 +0,0 @@
#ifdef __MINGW32__
# define __STDC_FORMAT_MACROS
#endif
#include <assert.h>
#include <inttypes.h>
#include <string>
#include <string.h>
#ifdef _WIN32
# include <direct.h>
# include <windows.h>
#else
# include <dirent.h>
# include <sys/types.h>
# include <unistd.h>
# include <errno.h>
#endif
#include <sys/stat.h>
#include "TracyStorage.hpp"
namespace tracy
{
static bool CreateDirStruct( const std::string& path )
{
struct stat buf;
if( stat( path.c_str(), &buf ) == 0 ) return true;
if( errno != ENOENT )
{
return false;
}
size_t pos = 0;
do
{
pos = path.find( '/', pos+1 );
#ifdef _WIN32
if( pos == 2 && path[1] == ':' ) continue; // Don't create drive name.
if( _mkdir( path.substr( 0, pos ).c_str() ) != 0 )
#else
if( mkdir( path.substr( 0, pos ).c_str(), S_IRWXU ) != 0 )
#endif
{
if( errno != EEXIST )
{
return false;
}
}
}
while( pos != std::string::npos );
return true;
}
static void GetConfigDirectory( char* buf, size_t& sz )
{
#ifdef _WIN32
auto path = getenv( "APPDATA" );
sz = strlen( path );
memcpy( buf, path, sz );
for( size_t i=0; i<sz; i++ )
{
if( buf[i] == '\\' )
{
buf[i] = '/';
}
}
#else
auto path = getenv( "XDG_CONFIG_HOME" );
if( path && *path )
{
sz = strlen( path );
memcpy( buf, path, sz );
}
else
{
path = getenv( "HOME" );
assert( path && *path );
sz = strlen( path );
memcpy( buf, path, sz );
memcpy( buf+sz, "/.config", 8 );
sz += 8;
}
#endif
}
const char* GetSavePath( const char* file )
{
assert( file && *file );
enum { Pool = 8 };
enum { MaxPath = 512 };
static char bufpool[Pool][MaxPath];
static int bufsel = 0;
char* buf = bufpool[bufsel];
bufsel = ( bufsel + 1 ) % Pool;
size_t sz;
GetConfigDirectory( buf, sz );
memcpy( buf+sz, "/tracy/", 8 );
sz += 7;
auto status = CreateDirStruct( buf );
assert( status );
const auto fsz = strlen( file );
assert( sz + fsz < MaxPath );
memcpy( buf+sz, file, fsz+1 );
return buf;
}
const char* GetSavePath( const char* program, uint64_t time, const char* file, bool create )
{
assert( program && *program );
enum { Pool = 8 };
enum { MaxPath = 512 };
static char bufpool[Pool][MaxPath];
static int bufsel = 0;
char* buf = bufpool[bufsel];
bufsel = ( bufsel + 1 ) % Pool;
size_t sz;
GetConfigDirectory( buf, sz );
const auto psz = strlen( program );
assert( psz < 512 );
char tmp[512];
strcpy( tmp, program );
for( size_t i=0; i<psz; i++ )
{
switch( tmp[i] )
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
case 0x7F:
case '<':
case '>':
case ':':
case '"':
case '/':
case '\\':
case '|':
case '?':
case '*':
tmp[i] = '_';
break;
default:
break;
}
}
// 604800 = 7 days
sz += sprintf( buf+sz, "/tracy/user/%c/%s/%" PRIu64 "/%" PRIu64 "/", tmp[0], tmp, uint64_t( time / 604800 ), time );
if( create )
{
auto status = CreateDirStruct( buf );
assert( status );
}
if( file )
{
const auto fsz = strlen( file );
assert( sz + fsz < MaxPath );
memcpy( buf+sz, file, fsz+1 );
}
else
{
buf[sz] = '\0';
}
return buf;
}
}

View File

@@ -1,14 +0,0 @@
#ifndef __TRACYSTORAGE_HPP__
#define __TRACYSTORAGE_HPP__
#include <stdint.h>
namespace tracy
{
const char* GetSavePath( const char* file );
const char* GetSavePath( const char* program, uint64_t time, const char* file, bool create );
}
#endif

View File

@@ -1,187 +0,0 @@
#include <inttypes.h>
#include <string.h>
#ifdef __EMSCRIPTEN__
# include <emscripten/html5.h>
# include <GLES2/gl2.h>
#else
# include <backends/imgui_impl_opengl3_loader.h>
#endif
#include "TracyTexture.hpp"
#include "../public/common/TracyForceInline.hpp"
#ifndef COMPRESSED_RGB_S3TC_DXT1_EXT
# define COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#endif
namespace tracy
{
static bool s_hardwareS3tc;
void InitTexture()
{
#ifdef __EMSCRIPTEN__
s_hardwareS3tc = emscripten_webgl_enable_extension( emscripten_webgl_get_current_context(), "WEBGL_compressed_texture_s3tc" );
#else
s_hardwareS3tc = false;
GLint num;
glGetIntegerv( GL_NUM_EXTENSIONS, &num );
for( GLint i=0; i<num; i++ )
{
auto ext = (const char*)glGetStringi( GL_EXTENSIONS, GLuint( i ) );
if( strcmp( ext, "GL_EXT_texture_compression_s3tc" ) == 0 )
{
s_hardwareS3tc = true;
break;
}
}
#endif
}
ImTextureID MakeTexture( bool zigzag )
{
GLuint tex;
glGenTextures( 1, &tex );
glBindTexture( GL_TEXTURE_2D, tex );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, zigzag ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, zigzag ? GL_REPEAT : GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
return tex;
}
void FreeTexture( ImTextureID _tex, void(*runOnMainThread)(const std::function<void()>&, bool) )
{
auto tex = (GLuint)_tex;
runOnMainThread( [tex] { glDeleteTextures( 1, &tex ); }, false );
}
static tracy_force_inline void DecodeDxt1Part( uint64_t d, uint32_t* dst, uint32_t w )
{
uint8_t* in = (uint8_t*)&d;
uint16_t c0, c1;
uint32_t idx;
memcpy( &c0, in, 2 );
memcpy( &c1, in+2, 2 );
memcpy( &idx, in+4, 4 );
uint8_t r0 = ( ( c0 & 0xF800 ) >> 8 ) | ( ( c0 & 0xF800 ) >> 13 );
uint8_t g0 = ( ( c0 & 0x07E0 ) >> 3 ) | ( ( c0 & 0x07E0 ) >> 9 );
uint8_t b0 = ( ( c0 & 0x001F ) << 3 ) | ( ( c0 & 0x001F ) >> 2 );
uint8_t r1 = ( ( c1 & 0xF800 ) >> 8 ) | ( ( c1 & 0xF800 ) >> 13 );
uint8_t g1 = ( ( c1 & 0x07E0 ) >> 3 ) | ( ( c1 & 0x07E0 ) >> 9 );
uint8_t b1 = ( ( c1 & 0x001F ) << 3 ) | ( ( c1 & 0x001F ) >> 2 );
uint32_t dict[4];
dict[0] = 0xFF000000 | ( b0 << 16 ) | ( g0 << 8 ) | r0;
dict[1] = 0xFF000000 | ( b1 << 16 ) | ( g1 << 8 ) | r1;
uint32_t r, g, b;
if( c0 > c1 )
{
r = (2*r0+r1)/3;
g = (2*g0+g1)/3;
b = (2*b0+b1)/3;
dict[2] = 0xFF000000 | ( b << 16 ) | ( g << 8 ) | r;
r = (2*r1+r0)/3;
g = (2*g1+g0)/3;
b = (2*b1+b0)/3;
dict[3] = 0xFF000000 | ( b << 16 ) | ( g << 8 ) | r;
}
else
{
r = (int(r0)+r1)/2;
g = (int(g0)+g1)/2;
b = (int(b0)+b1)/2;
dict[2] = 0xFF000000 | ( b << 16 ) | ( g << 8 ) | r;
dict[3] = 0xFF000000;
}
memcpy( dst+0, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+1, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+2, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+3, dict + (idx & 0x3), 4 );
idx >>= 2;
dst += w;
memcpy( dst+0, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+1, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+2, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+3, dict + (idx & 0x3), 4 );
idx >>= 2;
dst += w;
memcpy( dst+0, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+1, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+2, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+3, dict + (idx & 0x3), 4 );
idx >>= 2;
dst += w;
memcpy( dst+0, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+1, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+2, dict + (idx & 0x3), 4 );
idx >>= 2;
memcpy( dst+3, dict + (idx & 0x3), 4 );
}
void UpdateTexture( ImTextureID _tex, const char* data, int w, int h )
{
auto tex = (GLuint)_tex;
glBindTexture( GL_TEXTURE_2D, tex );
if( s_hardwareS3tc )
{
glCompressedTexImage2D( GL_TEXTURE_2D, 0, COMPRESSED_RGB_S3TC_DXT1_EXT, w, h, 0, w * h / 2, data );
}
else
{
auto tmp = new uint32_t[w*h];
auto src = (const uint64_t*)data;
auto dst = tmp;
for( int y=0; y<h/4; y++ )
{
for( int x=0; x<w/4; x++ )
{
uint64_t d = *src++;
DecodeDxt1Part( d, dst, w );
dst += 4;
}
dst += w*3;
}
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp );
delete[] tmp;
}
}
void UpdateTextureRGBA( ImTextureID _tex, void* data, int w, int h )
{
auto tex = (GLuint)_tex;
glBindTexture( GL_TEXTURE_2D, tex );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
}
void UpdateTextureRGBAMips( ImTextureID _tex, void** data, int* w, int* h, size_t mips )
{
auto tex = (GLuint)_tex;
glBindTexture( GL_TEXTURE_2D, tex );
for( size_t i=0; i<mips; i++ )
{
glTexImage2D( GL_TEXTURE_2D, i, GL_RGBA, w[i], h[i], 0, GL_RGBA, GL_UNSIGNED_BYTE, data[i] );
}
}
}

View File

@@ -1,19 +0,0 @@
#ifndef __TRACYTEXTURE_HPP__
#define __TRACYTEXTURE_HPP__
#include <functional>
#include <imgui.h>
namespace tracy
{
void InitTexture();
ImTextureID MakeTexture( bool zigzag = false );
void FreeTexture( ImTextureID tex, void(*runOnMainThread)(const std::function<void()>&, bool) );
void UpdateTexture( ImTextureID tex, const char* data, int w, int h );
void UpdateTextureRGBA( ImTextureID tex, void* data, int w, int h );
void UpdateTextureRGBAMips( ImTextureID tex, void** data, int* w, int* h, size_t mips );
}
#endif

View File

@@ -1,23 +0,0 @@
#ifndef __TRACYTIMELINECONTEXT_HPP__
#define __TRACYTIMELINECONTEXT_HPP__
#include <stdint.h>
#include "imgui.h"
namespace tracy
{
struct TimelineContext
{
float w, ty, sty, scale;
float yMin, yMax;
double pxns, nspx;
int64_t vStart, vEnd;
ImVec2 wpos;
bool hover;
};
}
#endif

View File

@@ -1,177 +0,0 @@
#include <algorithm>
#include <thread>
#include "TracyTimelineItem.hpp"
#include "TracyTimelineContext.hpp"
#include "TracyTimelineController.hpp"
#include "TracyView.hpp"
namespace tracy
{
TimelineController::TimelineController( View& view, Worker& worker, bool threading )
: m_height( 0 )
, m_scroll( 0 )
, m_centerItemkey( nullptr )
, m_centerItemOffsetY( 0 )
, m_firstFrame( true )
, m_view( view )
, m_worker( worker )
#ifdef __EMSCRIPTEN__
, m_td( threading ? 2 : 0, "Render" )
#else
, m_td( threading ? (size_t)std::max( 0, ( (int)std::thread::hardware_concurrency() - 2 ) / 2 ) : 0, "Render" )
#endif
{
}
TimelineController::~TimelineController()
{
}
void TimelineController::FirstFrameExpired()
{
m_firstFrame = false;
}
void TimelineController::Begin()
{
m_items.clear();
}
void TimelineController::UpdateCenterItem()
{
ImVec2 mousePos = ImGui::GetMousePos();
m_centerItemkey = nullptr;
m_centerItemOffsetY = 0;
if( m_firstFrame || !ImGui::IsMousePosValid( &mousePos ) ) return;
const auto timelineMousePosY = mousePos.y - ImGui::GetWindowPos().y;
int centerY = timelineMousePosY + ImGui::GetScrollY();
int yBegin = 0;
int yEnd = 0;
for( auto& item : m_items )
{
m_centerItemkey = item->GetKey();
yBegin = yEnd;
yEnd += item->GetHeight();
const auto inLowerBounds = m_centerItemkey == m_items.front()->GetKey() || yBegin <= centerY;
const auto inUpperBounds = m_centerItemkey == m_items.back()->GetKey() || centerY < yEnd;
if( inLowerBounds && inUpperBounds )
{
m_centerItemOffsetY = centerY - yBegin;
break;
}
}
}
std::optional<int> TimelineController::CalculateScrollPosition() const
{
if( !m_centerItemkey ) return std::nullopt;
ImVec2 mousePos = ImGui::GetMousePos();
if( !ImGui::IsMousePosValid( &mousePos ) ) return std::nullopt;
const auto timelineMousePosY = mousePos.y - ImGui::GetWindowPos().y;
int yBegin = 0;
int yEnd = 0;
for( auto& item : m_items )
{
yBegin = yEnd;
yEnd += item->GetHeight();
if( item->GetKey() != m_centerItemkey ) continue;
int scrollY = yBegin + m_centerItemOffsetY - timelineMousePosY;
return scrollY;
}
return std::nullopt;
}
void TimelineController::End( double pxns, const ImVec2& wpos, bool hover, bool vcenter, float yMin, float yMax, ImFont* smallFont )
{
auto shouldUpdateCenterItem = [&] () {
const auto imguiChangedScroll = m_scroll != ImGui::GetScrollY();
const auto& mouseDelta = ImGui::GetIO().MouseDelta;
const auto mouseMoved = mouseDelta.x != 0.0f || mouseDelta.y != 0.0f;
const auto& mousePos = ImGui::GetIO().MousePos;
const auto mouseVisible = ImGui::IsMousePosValid( &mousePos );
return ( ( imguiChangedScroll || mouseMoved || !mouseVisible ) && !ImGui::IsMouseDown( 1 ) ) || !m_centerItemkey;
};
if( !vcenter )
{
m_centerItemkey = nullptr;
m_centerItemOffsetY = 0;
}
else if( shouldUpdateCenterItem() )
{
UpdateCenterItem();
}
const auto& viewData = m_view.GetViewData();
TimelineContext ctx;
ctx.w = ImGui::GetContentRegionAvail().x - 1;
ctx.ty = ImGui::GetTextLineHeight();
ImGui::PushFont( smallFont );
ctx.sty = ImGui::GetTextLineHeight();
ImGui::PopFont();
ctx.scale = GetScale();
ctx.yMin = yMin;
ctx.yMax = yMax;
ctx.pxns = pxns;
ctx.nspx = 1.0 / pxns;
ctx.vStart = viewData.zvStart;
ctx.vEnd = viewData.zvEnd;
ctx.wpos = wpos;
ctx.hover = hover;
int yOffset = 0;
for( auto& item : m_items )
{
if( item->WantPreprocess() && item->IsVisible() )
{
const auto yPos = wpos.y + yOffset;
const bool visible = m_firstFrame || ( yPos < yMax && yPos + item->GetHeight() >= yMin );
item->Preprocess( ctx, m_td, visible, yPos );
}
yOffset += m_firstFrame ? 0 : item->GetHeight();
}
m_td.Sync();
yOffset = 0;
for( auto& item : m_items )
{
auto currentFrameItemHeight = item->GetHeight();
item->Draw( m_firstFrame, ctx, yOffset );
if( m_firstFrame ) currentFrameItemHeight = item->GetHeight();
yOffset += currentFrameItemHeight;
}
if( const auto scrollY = CalculateScrollPosition() )
{
int clampedScrollY = std::min<int>( *scrollY, std::max<int>( yOffset - ImGui::GetWindowHeight(), 0 ) );
ImGui::SetScrollY( clampedScrollY );
int minHeight = ImGui::GetWindowHeight() + clampedScrollY;
yOffset = std::max( yOffset, minHeight );
}
const auto scrollPos = ImGui::GetScrollY();
if( ( scrollPos == 0 && m_scroll != 0 ) || yOffset > m_height )
{
m_height = yOffset;
}
m_scroll = scrollPos;
}
}

View File

@@ -1,71 +0,0 @@
#ifndef __TRACYTIMELINECONTROLLER_HPP__
#define __TRACYTIMELINECONTROLLER_HPP__
#include <assert.h>
#include <optional>
#include <vector>
#include "TracyImGui.hpp"
#include "../server/TracyTaskDispatch.hpp"
#include "../server/tracy_robin_hood.h"
#include "../public/common/TracyForceInline.hpp"
namespace tracy
{
class TimelineItem;
class View;
class Worker;
class TimelineController
{
public:
TimelineController( View& view, Worker& worker, bool threading );
~TimelineController();
void FirstFrameExpired();
void Begin();
void End( double pxns, const ImVec2& wpos, bool hover, bool vcenter, float yMin, float yMax, ImFont* smallFont );
template<class T, class U>
void AddItem( U* data )
{
auto it = m_itemMap.find( data );
if( it == m_itemMap.end() ) it = m_itemMap.emplace( data, std::make_unique<T>( m_view, m_worker, data ) ).first;
m_items.emplace_back( it->second.get() );
}
float GetHeight() const { return m_height; }
const unordered_flat_map<const void*, std::unique_ptr<TimelineItem>>& GetItemMap() const { return m_itemMap; }
tracy_force_inline TimelineItem& GetItem( const void* data )
{
auto it = m_itemMap.find( data );
assert( it != m_itemMap.end() );
return *it->second;
}
private:
void UpdateCenterItem();
std::optional<int> CalculateScrollPosition() const;
std::vector<TimelineItem*> m_items;
unordered_flat_map<const void*, std::unique_ptr<TimelineItem>> m_itemMap;
float m_height;
float m_scroll;
const void* m_centerItemkey;
int m_centerItemOffsetY;
bool m_firstFrame;
View& m_view;
Worker& m_worker;
TaskDispatch m_td;
};
}
#endif

View File

@@ -1,105 +0,0 @@
#ifndef __TRACYTIMELINEDRAW_HPP__
#define __TRACYTIMELINEDRAW_HPP__
#include <stdint.h>
#include "TracyEvent.hpp"
#include "TracyShortPtr.hpp"
namespace tracy
{
enum class TimelineDrawType : uint8_t
{
Folded,
Zone,
GhostFolded,
Ghost
};
struct TimelineDraw
{
TimelineDrawType type;
uint16_t depth;
short_ptr<void*> ev;
Int48 rend;
uint32_t num;
uint32_t inheritedColor;
};
enum class ContextSwitchDrawType : uint8_t
{
Waiting,
Folded,
Running
};
struct ContextSwitchDraw
{
ContextSwitchDrawType type;
uint32_t idx;
uint32_t data; // Folded: number of items -OR- Waiting: wait stack
};
struct SamplesDraw
{
uint32_t num;
uint32_t idx;
};
struct MessagesDraw
{
short_ptr<MessageData> msg;
bool highlight;
uint32_t num;
};
struct CpuUsageDraw
{
int own;
int other;
};
struct CpuCtxDraw
{
uint32_t idx;
uint32_t num;
};
struct LockState
{
enum Type : uint8_t
{
Nothing = 1 << 0,
HasLock = 1 << 1, // green
HasBlockingLock = 1 << 2, // yellow
WaitLock = 1 << 3 // red
};
};
struct LockDrawItem
{
Int48 t1;
LockState::Type state;
uint32_t condensed;
short_ptr<LockEventPtr> ptr, next;
};
struct LockDraw
{
uint32_t id;
bool forceDraw;
uint8_t thread;
std::vector<LockDrawItem> data;
};
}
#endif

View File

@@ -1,168 +0,0 @@
#include <algorithm>
#include "TracyImGui.hpp"
#include "TracyMouse.hpp"
#include "TracyTimelineContext.hpp"
#include "TracyTimelineItem.hpp"
#include "TracyView.hpp"
namespace tracy
{
TimelineItem::TimelineItem( View& view, Worker& worker, const void* key, bool wantPreprocess )
: m_visible( true )
, m_showFull( true )
, m_height( 0 )
, m_wantPreprocess( wantPreprocess )
, m_key( key )
, m_view( view )
, m_worker( worker )
{
}
void TimelineItem::Draw( bool firstFrame, const TimelineContext& ctx, int yOffset )
{
const auto yBegin = yOffset;
auto yEnd = yOffset;
if( !IsVisible() )
{
DrawFinished();
if( m_height != 0 ) AdjustThreadHeight( firstFrame, yBegin, yEnd );
return;
}
if( IsEmpty() )
{
DrawFinished();
return;
}
const auto w = ctx.w;
const auto ty = ctx.ty;
const auto ostep = ty + 1;
const auto& wpos = ctx.wpos;
const auto yPos = wpos.y + yBegin;
const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
auto draw = ImGui::GetWindowDrawList();
ImGui::PushID( this );
ImGui::PushClipRect( wpos + ImVec2( 0, yBegin ), wpos + ImVec2( w, yBegin + m_height ), true );
yEnd += ostep;
if( m_showFull )
{
if( !DrawContents( ctx, yEnd ) && !m_view.GetViewData().drawEmptyLabels )
{
DrawFinished();
yEnd = yBegin;
AdjustThreadHeight( firstFrame, yBegin, yEnd );
ImGui::PopClipRect();
ImGui::PopID();
return;
}
}
DrawOverlay( wpos + ImVec2( 0, yBegin ), wpos + ImVec2( w, yEnd ) );
ImGui::PopClipRect();
float labelWidth;
const auto hdrOffset = yBegin;
const bool drawHeader = yPos + ty >= ctx.yMin && yPos <= ctx.yMax;
if( drawHeader )
{
const auto color = HeaderColor();
const auto colorInactive = HeaderColorInactive();
if( m_showFull )
{
DrawTextContrast( draw, wpos + ImVec2( 0, hdrOffset ), color, ICON_FA_CARET_DOWN );
}
else
{
DrawTextContrast( draw, wpos + ImVec2( 0, hdrOffset ), colorInactive, ICON_FA_CARET_RIGHT );
}
const auto label = HeaderLabel();
labelWidth = ImGui::CalcTextSize( label ).x;
DrawTextContrast( draw, wpos + ImVec2( ty, hdrOffset ), m_showFull ? color : colorInactive, label );
if( m_showFull )
{
DrawLine( draw, dpos + ImVec2( 0, hdrOffset + ty - 1 ), dpos + ImVec2( w, hdrOffset + ty - 1 ), HeaderLineColor() );
HeaderExtraContents( ctx, hdrOffset, labelWidth );
}
if( ctx.hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, hdrOffset ), wpos + ImVec2( ty + labelWidth, hdrOffset + ty ) ) )
{
HeaderTooltip( label );
if( IsMouseClicked( 0 ) )
{
m_showFull = !m_showFull;
}
if( IsMouseClicked( 2 ) )
{
const auto t0 = RangeBegin();
const auto t1 = RangeEnd();
if( t0 < t1 )
{
m_view.ZoomToRange( t0, t1 );
}
}
if( IsMouseClicked( 1 ) )
{
ImGui::OpenPopup( "menuPopup" );
}
}
}
if( ImGui::BeginPopup( "menuPopup" ) )
{
if( ImGui::MenuItem( ICON_FA_EYE_SLASH " Hide" ) )
{
SetVisible( false );
ImGui::CloseCurrentPopup();
}
DrawExtraPopupItems();
ImGui::EndPopup();
}
yEnd += 0.2f * ostep;
AdjustThreadHeight( firstFrame, yBegin, yEnd );
DrawFinished();
ImGui::PopID();
}
void TimelineItem::AdjustThreadHeight( bool firstFrame, int yBegin, int yEnd )
{
const auto speed = 4.0;
const auto baseMove = 1.0;
const auto newHeight = yEnd - yBegin;
if( firstFrame )
{
m_height = newHeight;
}
else if( m_height != newHeight )
{
const auto diff = newHeight - m_height;
const auto preClampMove = diff * speed * ImGui::GetIO().DeltaTime;
if( diff > 0 )
{
const auto move = preClampMove + baseMove;
m_height = int( std::min<double>( m_height + move, newHeight ) );
}
else
{
const auto move = preClampMove - baseMove;
m_height = int( std::max<double>( m_height + move, newHeight ) );
}
s_wasActive = true;
}
}
void TimelineItem::VisibilityCheckbox()
{
SmallCheckbox( HeaderLabel(), &m_visible );
}
}

View File

@@ -1,77 +0,0 @@
#ifndef __TRACYTIMELINEITEM_HPP__
#define __TRACYTIMELINEITEM_HPP__
#include <assert.h>
#include <stdint.h>
#include "imgui.h"
namespace tracy
{
struct TimelineContext;
class TaskDispatch;
class View;
class Worker;
class TimelineItem
{
public:
TimelineItem( View& view, Worker& worker, const void* key, bool wantPreprocess );
virtual ~TimelineItem() = default;
// draws the timeline item and also updates the next frame height value
void Draw( bool firstFrame, const TimelineContext& ctx, int yOffset );
bool WantPreprocess() const { return m_wantPreprocess; }
virtual void Preprocess( const TimelineContext& ctx, TaskDispatch& td, bool visible, int yPos ) { assert( false ); }
void VisibilityCheckbox();
virtual void SetVisible( bool visible ) { m_visible = visible; }
virtual bool IsVisible() const { return m_visible; }
void SetShowFull( bool showFull ) { m_showFull = showFull; }
// returns 0 instead of the correct value for the first frame
int GetHeight() const { return m_height; }
const void* GetKey() const { return m_key; }
protected:
virtual uint32_t HeaderColor() const = 0;
virtual uint32_t HeaderColorInactive() const = 0;
virtual uint32_t HeaderLineColor() const = 0;
virtual const char* HeaderLabel() const = 0;
virtual void HeaderTooltip( const char* label ) const {};
virtual void HeaderExtraContents( const TimelineContext& ctx, int offset, float labelWidth ) {};
virtual int64_t RangeBegin() const = 0;
virtual int64_t RangeEnd() const = 0;
virtual bool DrawContents( const TimelineContext& ctx, int& offset ) = 0;
virtual void DrawOverlay( const ImVec2& ul, const ImVec2& dr ) {}
virtual void DrawExtraPopupItems() {}
virtual void DrawFinished() {}
virtual bool IsEmpty() const { return false; }
bool m_visible;
bool m_showFull;
private:
void AdjustThreadHeight( bool firstFrame, int yBegin, int yEnd );
int m_height;
bool m_wantPreprocess;
const void* m_key;
protected:
View& m_view;
Worker& m_worker;
};
}
#endif

View File

@@ -1,259 +0,0 @@
#include "TracyImGui.hpp"
#include "TracyPrint.hpp"
#include "TracyTimelineContext.hpp"
#include "TracyTimelineItemCpuData.hpp"
#include "TracyUtility.hpp"
#include "TracyView.hpp"
#include "TracyWorker.hpp"
namespace tracy
{
TimelineItemCpuData::TimelineItemCpuData( View& view, Worker& worker, void* key )
: TimelineItem( view, worker, key, true )
{
}
void TimelineItemCpuData::SetVisible( bool visible )
{
m_view.GetViewData().drawCpuData = visible;
}
bool TimelineItemCpuData::IsVisible() const
{
return m_view.GetViewData().drawCpuData;
}
bool TimelineItemCpuData::IsEmpty() const
{
return m_worker.GetCpuDataCpuCount() == 0;
}
int64_t TimelineItemCpuData::RangeBegin() const
{
return -1;
}
int64_t TimelineItemCpuData::RangeEnd() const
{
return -1;
}
bool TimelineItemCpuData::DrawContents( const TimelineContext& ctx, int& offset )
{
m_view.DrawCpuData( ctx, m_cpuDraw, m_ctxDraw, offset, m_hasCpuData );
return true;
}
void TimelineItemCpuData::DrawFinished()
{
m_cpuDraw.clear();
for( auto& v : m_ctxDraw ) v.clear();
}
void TimelineItemCpuData::Preprocess( const TimelineContext& ctx, TaskDispatch& td, bool visible, int yPos )
{
assert( m_cpuDraw.empty() );
for( auto& v : m_ctxDraw ) assert( v.empty() );
if( !visible ) return;
const auto yMin = ctx.yMin;
const auto yMax = ctx.yMax;
const auto ty = ctx.ty;
const auto sty = ctx.sty;
const auto ostep = ty + 1;
const auto sstep = sty + 1;
bool hasCpuData = false;
auto pos = yPos + ostep;
#ifdef TRACY_NO_STATISTICS
if( m_view.GetViewData().drawCpuUsageGraph )
#else
if( m_view.GetViewData().drawCpuUsageGraph && m_worker.IsCpuUsageReady() )
#endif
{
#ifndef TRACY_NO_STATISTICS
auto& ctxUsage = m_worker.GetCpuUsage();
if( !ctxUsage.empty() )
{
hasCpuData = true;
}
else
#endif
{
const auto cpuDataCount = m_worker.GetCpuDataCpuCount();
const auto cpuData = m_worker.GetCpuData();
for( int i=0; i<cpuDataCount; i++ )
{
if( !cpuData[i].cs.empty() )
{
hasCpuData = true;
break;
}
}
}
if( hasCpuData )
{
const auto cpuUsageHeight = floor( 30.f * GetScale() );
if( pos <= yMax && pos + cpuUsageHeight + 3 >= yMin )
{
td.Queue( [this, &ctx] {
PreprocessCpuUsage( ctx );
} );
}
pos += cpuUsageHeight + 3;
}
}
m_hasCpuData = hasCpuData;
auto cpuData = m_worker.GetCpuData();
const auto cpuCnt = m_worker.GetCpuDataCpuCount();
if( m_ctxDraw.size() != cpuCnt ) m_ctxDraw.resize( cpuCnt );
for( int i=0; i<cpuCnt; i++ )
{
auto& cs = cpuData[i].cs;
if( !cs.empty() && pos <= yMax && pos + sty >= yMin )
{
td.Queue( [this, &ctx, &cs, i] {
PreprocessCpuCtxSwitches( ctx, cs, m_ctxDraw[i] );
} );
}
pos += sstep;
}
}
constexpr float MinVisSize = 3;
void TimelineItemCpuData::PreprocessCpuCtxSwitches( const TimelineContext& ctx, const Vector<ContextSwitchCpu>& cs, std::vector<CpuCtxDraw>& out )
{
const auto vStart = ctx.vStart;
const auto vEnd = ctx.vEnd;
const auto nspx = ctx.nspx;
auto it = std::lower_bound( cs.begin(), cs.end(), std::max<int64_t>( 0, vStart ), [] ( const auto& l, const auto& r ) { return ( l.IsEndValid() ? l.End() : l.Start() ) < r; } );
if( it == cs.end() ) return;
auto eit = std::lower_bound( it, cs.end(), vEnd, [] ( const auto& l, const auto& r ) { return l.Start() < r; } );
if( it == eit ) return;
const auto MinVisNs = int64_t( round( GetScale() * MinVisSize * nspx ) );
while( it < eit )
{
const auto end = it->IsEndValid() ? it->End() : it->Start();
const auto zsz = end - it->Start();
if( zsz < MinVisNs )
{
auto nextTime = end + MinVisNs;
auto next = it + 1;
for(;;)
{
next = std::lower_bound( next, eit, nextTime, [] ( const auto& l, const auto& r ) { return ( l.IsEndValid() ? l.End() : l.Start() ) < r; } );
if( next == eit ) break;
auto prev = next - 1;
const auto pt = prev->IsEndValid() ? prev->End() : prev->Start();
const auto nt = next->IsEndValid() ? next->End() : next->Start();
if( nt - pt >= MinVisNs ) break;
nextTime = nt + MinVisNs;
}
out.emplace_back( CpuCtxDraw { uint32_t( it - cs.begin() ), uint32_t( next - it ) } );
it = next;
}
else
{
out.emplace_back( CpuCtxDraw { uint32_t( it - cs.begin() ), 0 } );
++it;
}
}
}
void TimelineItemCpuData::PreprocessCpuUsage( const TimelineContext& ctx )
{
const auto vStart = ctx.vStart;
const auto nspx = ctx.nspx;
const auto w = ctx.w;
const auto num = size_t( w );
if( vStart > m_worker.GetLastTime() || int64_t( vStart + nspx * num ) < 0 ) return;
const auto lastTime = m_worker.GetLastTime();
#ifndef TRACY_NO_STATISTICS
auto& ctxUsage = m_worker.GetCpuUsage();
if( !ctxUsage.empty() )
{
auto itBegin = ctxUsage.begin();
for( size_t i=0; i<num; i++ )
{
const auto time = int64_t( vStart + nspx * i );
if( time > lastTime ) return;
if( time < 0 )
{
m_cpuDraw.emplace_back( CpuUsageDraw { 0, 0 } );
}
else
{
const auto test = ( time << 16 ) | 0xFFFF;
auto it = std::upper_bound( itBegin, ctxUsage.end(), test, [] ( const auto& l, const auto& r ) { return l < r._time_other_own; } );
if( it == ctxUsage.end() ) return;
if( it == ctxUsage.begin() )
{
m_cpuDraw.emplace_back( CpuUsageDraw { 0, 0 } );
}
else
{
--it;
m_cpuDraw.emplace_back( CpuUsageDraw { it->Own(), it->Other() } );
}
itBegin = it;
}
}
}
else
#endif
{
m_cpuDraw.resize( num );
memset( m_cpuDraw.data(), 0, sizeof( CpuUsageDraw ) * num );
const auto pid = m_worker.GetPid();
const auto cpuDataCount = m_worker.GetCpuDataCpuCount();
const auto cpuData = m_worker.GetCpuData();
for( int i=0; i<cpuDataCount; i++ )
{
auto& cs = cpuData[i].cs;
if( !cs.empty() )
{
auto itBegin = cs.begin();
auto ptr = m_cpuDraw.data();
for( size_t i=0; i<num; i++ )
{
const auto time = int64_t( vStart + nspx * i );
if( time > lastTime ) break;
if( time >= 0 )
{
auto it = std::lower_bound( itBegin, cs.end(), time, [] ( const auto& l, const auto& r ) { return (uint64_t)l.End() < (uint64_t)r; } );
if( it == cs.end() ) break;
if( it->IsEndValid() && it->Start() <= time )
{
if( m_worker.GetPidFromTid( m_worker.DecompressThreadExternal( it->Thread() ) ) == pid )
{
ptr->own++;
}
else
{
ptr->other++;
}
}
itBegin = it;
}
ptr++;
}
}
}
}
}
}

View File

@@ -1,47 +0,0 @@
#ifndef __TRACYTIMELINEITEMCPUDATA_HPP__
#define __TRACYTIMELINEITEMCPUDATA_HPP__
#include "TracyEvent.hpp"
#include "TracyTimelineItem.hpp"
#include "TracyTimelineDraw.hpp"
namespace tracy
{
class TimelineItemCpuData final : public TimelineItem
{
public:
TimelineItemCpuData( View& view, Worker& worker, void* key );
void SetVisible( bool visible ) override;
bool IsVisible() const override;
protected:
uint32_t HeaderColor() const override { return 0xFFDD88DD; }
uint32_t HeaderColorInactive() const override { return 0xFF6E446E; }
uint32_t HeaderLineColor() const override { return 0x66DD88DD; }
const char* HeaderLabel() const override { return "CPU data"; }
int64_t RangeBegin() const override;
int64_t RangeEnd() const override;
bool DrawContents( const TimelineContext& ctx, int& offset ) override;
void DrawFinished() override;
bool IsEmpty() const override;
void Preprocess( const TimelineContext& ctx, TaskDispatch& td, bool visible, int yPos ) override;
private:
void PreprocessCpuUsage( const TimelineContext& ctx );
void PreprocessCpuCtxSwitches( const TimelineContext& ctx, const Vector<ContextSwitchCpu>& cs, std::vector<CpuCtxDraw>& out );
std::vector<CpuUsageDraw> m_cpuDraw;
std::vector<std::vector<CpuCtxDraw>> m_ctxDraw;
bool m_hasCpuData;
};
}
#endif

View File

@@ -1,198 +0,0 @@
#include "TracyImGui.hpp"
#include "TracyPopcnt.hpp"
#include "TracyPrint.hpp"
#include "TracyTimelineContext.hpp"
#include "TracyTimelineItemGpu.hpp"
#include "TracyUtility.hpp"
#include "TracyView.hpp"
#include "TracyWorker.hpp"
namespace tracy
{
TimelineItemGpu::TimelineItemGpu( View& view, Worker& worker, GpuCtxData* gpu )
: TimelineItem( view, worker, gpu, false )
, m_gpu( gpu )
, m_idx( view.GetNextGpuIdx() )
{
}
bool TimelineItemGpu::IsEmpty() const
{
return m_gpu->threadData.empty();
}
const char* TimelineItemGpu::HeaderLabel() const
{
static char buf[4096];
if( m_gpu->name.Active() )
{
sprintf( buf, "%s", m_worker.GetString( m_gpu->name ) );
}
else
{
sprintf( buf, "%s context %i", GpuContextNames[(int)m_gpu->type], m_idx );
}
return buf;
}
void TimelineItemGpu::HeaderTooltip( const char* label ) const
{
const bool dynamicColors = m_view.GetViewData().dynamicColors;
const bool isMultithreaded =
( m_gpu->type == GpuContextType::Vulkan ) ||
( m_gpu->type == GpuContextType::OpenCL ) ||
( m_gpu->type == GpuContextType::Direct3D12 ) ||
( m_gpu->type == GpuContextType::Metal );
char buf[64];
sprintf( buf, "%s context %i", GpuContextNames[(int)m_gpu->type], m_idx );
ImGui::BeginTooltip();
if( m_gpu->name.Active() ) TextFocused( "Name:", m_worker.GetString( m_gpu->name ) );
ImGui::TextUnformatted( buf );
ImGui::Separator();
if( !isMultithreaded )
{
SmallColorBox( GetThreadColor( m_gpu->thread, 0, dynamicColors ) );
ImGui::SameLine();
TextFocused( "Thread:", m_worker.GetThreadName( m_gpu->thread ) );
}
else
{
if( m_gpu->threadData.size() == 1 )
{
auto it = m_gpu->threadData.begin();
auto tid = it->first;
if( tid == 0 )
{
if( !it->second.timeline.empty() )
{
if( it->second.timeline.is_magic() )
{
auto& tl = *(Vector<GpuEvent>*)&it->second.timeline;
tid = m_worker.DecompressThread( tl.begin()->Thread() );
}
else
{
tid = m_worker.DecompressThread( (*it->second.timeline.begin())->Thread() );
}
}
}
SmallColorBox( GetThreadColor( tid, 0, dynamicColors ) );
ImGui::SameLine();
TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( tid ) );
if( m_worker.IsThreadFiber( tid ) )
{
ImGui::SameLine();
TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
}
}
else
{
ImGui::TextDisabled( "Threads:" );
ImGui::Indent();
for( auto& td : m_gpu->threadData )
{
SmallColorBox( GetThreadColor( td.first, 0, dynamicColors ) );
ImGui::SameLine();
ImGui::TextUnformatted( m_worker.GetThreadName( td.first ) );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( td.first ) );
}
ImGui::Unindent();
}
}
const auto t0 = RangeBegin();
if( t0 != std::numeric_limits<int64_t>::max() )
{
TextFocused( "Appeared at", TimeToString( t0 ) );
}
TextFocused( "Zone count:", RealToString( m_gpu->count ) );
if( m_gpu->period != 1.f )
{
TextFocused( "Timestamp accuracy:", TimeToString( m_gpu->period ) );
}
if( m_gpu->overflow != 0 )
{
ImGui::Separator();
ImGui::TextUnformatted( "GPU timer overflow has been detected." );
TextFocused( "Timer resolution:", RealToString( 63 - TracyLzcnt( m_gpu->overflow ) ) );
ImGui::SameLine();
TextDisabledUnformatted( "bits" );
}
ImGui::EndTooltip();
}
void TimelineItemGpu::HeaderExtraContents( const TimelineContext& ctx, int offset, float labelWidth )
{
if( m_gpu->name.Active() )
{
auto draw = ImGui::GetWindowDrawList();
const auto ty = ImGui::GetTextLineHeight();
char buf[64];
sprintf( buf, "%s context %i", GpuContextNames[(int)m_gpu->type], m_idx );
draw->AddText( ctx.wpos + ImVec2( ty * 1.5f + labelWidth, offset ), HeaderColorInactive(), buf );
}
}
int64_t TimelineItemGpu::RangeBegin() const
{
int64_t t = std::numeric_limits<int64_t>::max();
for( auto& td : m_gpu->threadData )
{
int64_t t0;
if( td.second.timeline.is_magic() )
{
t0 = ((Vector<GpuEvent>*)&td.second.timeline)->front().GpuStart();
}
else
{
t0 = td.second.timeline.front()->GpuStart();
}
if( t0 >= 0 )
{
t = std::min( t, t0 );
}
}
return t;
}
int64_t TimelineItemGpu::RangeEnd() const
{
int64_t t = std::numeric_limits<int64_t>::min();
for( auto& td : m_gpu->threadData )
{
int64_t t0;
if( td.second.timeline.is_magic() )
{
t0 = ((Vector<GpuEvent>*)&td.second.timeline)->front().GpuStart();
}
else
{
t0 = td.second.timeline.front()->GpuStart();
}
if( t0 >= 0 )
{
if( td.second.timeline.is_magic() )
{
t = std::max( t, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( ((Vector<GpuEvent>*)&td.second.timeline)->back() ) ) );
}
else
{
t = std::max( t, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( *td.second.timeline.back() ) ) );
}
}
}
return t;
}
bool TimelineItemGpu::DrawContents( const TimelineContext& ctx, int& offset )
{
return m_view.DrawGpu( ctx, *m_gpu, offset );
}
}

View File

@@ -1,40 +0,0 @@
#ifndef __TRACYTIMELINEITEMGPU_HPP__
#define __TRACYTIMELINEITEMGPU_HPP__
#include "TracyEvent.hpp"
#include "TracyTimelineItem.hpp"
namespace tracy
{
class TimelineItemGpu final : public TimelineItem
{
public:
TimelineItemGpu( View& view, Worker& worker, GpuCtxData* gpu );
int GetIdx() const { return m_idx; }
protected:
uint32_t HeaderColor() const override { return 0xFFFFAAAA; }
uint32_t HeaderColorInactive() const override { return 0xFF886666; }
uint32_t HeaderLineColor() const override { return 0x33FFFFFF; }
const char* HeaderLabel() const override;
int64_t RangeBegin() const override;
int64_t RangeEnd() const override;
void HeaderTooltip( const char* label ) const override;
void HeaderExtraContents( const TimelineContext& ctx, int offset, float labelWidth ) override;
bool DrawContents( const TimelineContext& ctx, int& offset ) override;
bool IsEmpty() const override;
private:
GpuCtxData* m_gpu;
int m_idx;
};
}
#endif

View File

@@ -1,252 +0,0 @@
#include "TracyImGui.hpp"
#include "TracyPrint.hpp"
#include "TracyTimelineContext.hpp"
#include "TracyTimelineItemPlot.hpp"
#include "TracyUtility.hpp"
#include "TracyView.hpp"
#include "TracyWorker.hpp"
#include "tracy_pdqsort.h"
namespace tracy
{
constexpr int PlotHeightPx = 100;
constexpr int MinVisSize = 3;
TimelineItemPlot::TimelineItemPlot( View& view, Worker& worker, PlotData* plot )
: TimelineItem( view, worker, plot, true )
, m_plot( plot )
{
}
bool TimelineItemPlot::IsEmpty() const
{
return m_plot->data.empty();
}
const char* TimelineItemPlot::HeaderLabel() const
{
static char tmp[1024];
switch( m_plot->type )
{
case PlotType::User:
return m_worker.GetString( m_plot->name );
case PlotType::Memory:
if( m_plot->name == 0 )
{
return ICON_FA_MEMORY " Memory usage";
}
else
{
sprintf( tmp, ICON_FA_MEMORY " %s", m_worker.GetString( m_plot->name ) );
return tmp;
}
case PlotType::SysTime:
return ICON_FA_GAUGE_HIGH " CPU usage";
case PlotType::Power:
sprintf( tmp, ICON_FA_BOLT " %s", m_worker.GetString( m_plot->name ) );
return tmp;
default:
assert( false );
return nullptr;
}
}
void TimelineItemPlot::HeaderTooltip( const char* label ) const
{
ImGui::BeginTooltip();
SmallColorBox( GetPlotColor( *m_plot, m_worker ) );
ImGui::SameLine();
TextFocused( "Plot", label );
ImGui::Separator();
const auto first = RangeBegin();
const auto last = RangeEnd();
const auto activity = last - first;
const auto traceLen = m_worker.GetLastTime() - m_worker.GetFirstTime();
TextFocused( "Appeared at", TimeToString( first ) );
TextFocused( "Last event at", TimeToString( last ) );
TextFocused( "Activity time:", TimeToString( activity ) );
ImGui::SameLine();
char buf[64];
PrintStringPercent( buf, activity / double( traceLen ) * 100 );
TextDisabledUnformatted( buf );
ImGui::Separator();
TextFocused( "Data points:", RealToString( m_plot->data.size() ) );
TextFocused( "Data range:", FormatPlotValue( m_plot->max - m_plot->min, m_plot->format ) );
TextFocused( "Min value:", FormatPlotValue( m_plot->min, m_plot->format ) );
TextFocused( "Max value:", FormatPlotValue( m_plot->max, m_plot->format ) );
TextFocused( "Avg value:", FormatPlotValue( m_plot->sum / m_plot->data.size(), m_plot->format ) );
TextFocused( "Data/second:", RealToString( double( m_plot->data.size() ) / activity * 1000000000ll ) );
const auto it = std::lower_bound( m_plot->data.begin(), m_plot->data.end(), last - 1000000000ll * 10, [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } );
const auto tr10 = last - it->time.Val();
if( tr10 != 0 )
{
TextFocused( "D/s (10s):", RealToString( double( std::distance( it, m_plot->data.end() ) ) / tr10 * 1000000000ll ) );
}
ImGui::EndTooltip();
}
void TimelineItemPlot::HeaderExtraContents( const TimelineContext& ctx, int offset, float labelWidth )
{
auto draw = ImGui::GetWindowDrawList();
const auto ty = ImGui::GetTextLineHeight();
char tmp[128];
sprintf( tmp, "(y-range: %s, visible data points: %s)", FormatPlotValue( m_plot->rMax - m_plot->rMin, m_plot->format ), RealToString( m_plot->num ) );
draw->AddText( ctx.wpos + ImVec2( ty * 1.5f + labelWidth, offset ), 0xFF226E6E, tmp );
}
int64_t TimelineItemPlot::RangeBegin() const
{
return m_plot->data.front().time.Val();
}
int64_t TimelineItemPlot::RangeEnd() const
{
return m_plot->data.back().time.Val();
}
bool TimelineItemPlot::DrawContents( const TimelineContext& ctx, int& offset )
{
return m_view.DrawPlot( ctx, *m_plot, m_draw, offset, m_rightEnd );
}
void TimelineItemPlot::DrawFinished()
{
m_draw.clear();
}
void TimelineItemPlot::Preprocess( const TimelineContext& ctx, TaskDispatch& td, bool visible, int yPos )
{
assert( m_draw.empty() );
if( !visible ) return;
if( yPos > ctx.yMax ) return;
if( m_plot->data.empty() ) return;
const auto PlotHeight = int( round( PlotHeightPx * GetScale() ) );
if( yPos + PlotHeight < ctx.yMin ) return;
td.Queue( [this, &ctx] {
const auto vStart = ctx.vStart;
const auto vEnd = ctx.vEnd;
const auto nspx = ctx.nspx;
const auto MinVisNs = int64_t( round( MinVisSize * nspx ) );
auto& vec = m_plot->data;
vec.ensure_sorted();
if( vec.front().time.Val() > vEnd )
{
m_plot->rMin = 0;
m_plot->rMax = 0;
m_plot->num = 0;
m_rightEnd = false;
return;
}
else if( vec.back().time.Val() < vStart )
{
const auto lastTime = m_worker.GetLastTime();
const auto val = vec.back().val;
m_plot->rMin = val - 1;
m_plot->rMax = val + 1;
m_plot->num = lastTime < vStart ? 0 : 1;
m_rightEnd = vec.back().time.Val() < lastTime;
return;
}
auto it = std::lower_bound( vec.begin(), vec.end(), vStart, [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } );
auto end = std::lower_bound( it, vec.end(), vEnd, [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } );
m_rightEnd = end == vec.end() && vec.back().time.Val() < m_worker.GetLastTime();
if( end != vec.end() ) end++;
if( it != vec.begin() ) it--;
double min = it->val;
double max = it->val;
const auto num = end - it;
if( num > 1000000 )
{
min = m_plot->min;
max = m_plot->max;
}
else
{
auto tmp = it;
while( ++tmp < end )
{
if( tmp->val < min ) min = tmp->val;
else if( tmp->val > max ) max = tmp->val;
}
}
if( min == max )
{
min--;
max++;
}
m_plot->rMin = min;
m_plot->rMax = max;
m_plot->num = num;
m_draw.emplace_back( 0 );
m_draw.emplace_back( it - vec.begin() );
++it;
while( it < end )
{
auto next = std::upper_bound( it, end, int64_t( it->time.Val() + MinVisNs ), [] ( const auto& l, const auto& r ) { return l < r.time.Val(); } );
assert( next > it );
const auto rsz = uint32_t( next - it );
if( rsz < 4 )
{
for( int i=0; i<rsz; i++ )
{
m_draw.emplace_back( 0 );
m_draw.emplace_back( it - vec.begin() );
++it;
}
}
else
{
// Sync with View::DrawPlot()!
constexpr int NumSamples = 256;
uint32_t samples[NumSamples];
uint32_t cnt = 0;
uint32_t offset = it - vec.begin();
if( rsz < NumSamples )
{
for( cnt=0; cnt<rsz; cnt++ )
{
samples[cnt] = offset + cnt;
}
}
else
{
const auto skip = ( rsz + NumSamples - 1 ) / NumSamples;
const auto limit = rsz / skip;
for( cnt=0; cnt<limit; cnt++ )
{
samples[cnt] = offset + cnt * skip;
}
if( cnt == limit ) cnt--;
samples[cnt++] = offset + rsz - 1;
}
it = next;
pdqsort_branchless( samples, samples+cnt, [&vec] ( const auto& l, const auto& r ) { return vec[l].val < vec[r].val; } );
assert( rsz > 0 );
m_draw.emplace_back( rsz );
m_draw.emplace_back( offset );
m_draw.emplace_back( samples[0] );
m_draw.emplace_back( samples[cnt-1] );
}
}
} );
}
}

View File

@@ -1,44 +0,0 @@
#ifndef __TRACYTIMELINEITEMPLOT_HPP__
#define __TRACYTIMELINEITEMPLOT_HPP__
#include "TracyEvent.hpp"
#include "TracyTimelineDraw.hpp"
#include "TracyTimelineItem.hpp"
namespace tracy
{
class TimelineItemPlot final : public TimelineItem
{
public:
TimelineItemPlot( View& view, Worker& worker, PlotData* plot );
protected:
uint32_t HeaderColor() const override { return 0xFF44DDDD; }
uint32_t HeaderColorInactive() const override { return 0xFF226E6E; }
uint32_t HeaderLineColor() const override { return 0x8844DDDD; }
const char* HeaderLabel() const override;
int64_t RangeBegin() const override;
int64_t RangeEnd() const override;
void HeaderTooltip( const char* label ) const override;
void HeaderExtraContents( const TimelineContext& ctx, int offset, float labelWidth ) override;
bool DrawContents( const TimelineContext& ctx, int& offset ) override;
void DrawFinished() override;
bool IsEmpty() const override;
void Preprocess( const TimelineContext& ctx, TaskDispatch& td, bool visible, int yPos ) override;
private:
PlotData* m_plot;
std::vector<uint32_t> m_draw;
bool m_rightEnd;
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,67 +0,0 @@
#ifndef __TRACYTIMELINEITEMTHREAD_HPP__
#define __TRACYTIMELINEITEMTHREAD_HPP__
#include "TracyEvent.hpp"
#include "TracyTimelineDraw.hpp"
#include "TracyTimelineItem.hpp"
namespace tracy
{
class TimelineItemThread final : public TimelineItem
{
public:
TimelineItemThread( View& view, Worker& worker, const ThreadData* plot );
protected:
uint32_t HeaderColor() const override;
uint32_t HeaderColorInactive() const override;
uint32_t HeaderLineColor() const override;
const char* HeaderLabel() const override;
int64_t RangeBegin() const override;
int64_t RangeEnd() const override;
void HeaderTooltip( const char* label ) const override;
void HeaderExtraContents( const TimelineContext& ctx, int offset, float labelWidth ) override;
bool DrawContents( const TimelineContext& ctx, int& offset ) override;
void DrawOverlay( const ImVec2& ul, const ImVec2& dr ) override;
void DrawExtraPopupItems() override;
void DrawFinished() override;
bool IsEmpty() const override;
void Preprocess( const TimelineContext& ctx, TaskDispatch& td, bool visible, int yPos ) override;
private:
#ifndef TRACY_NO_STATISTICS
int PreprocessGhostLevel( const TimelineContext& ctx, const Vector<GhostZone>& vec, int depth, bool visible );
#endif
int PreprocessZoneLevel( const TimelineContext& ctx, const Vector<short_ptr<ZoneEvent>>& vec, int depth, bool visible, const uint32_t inheritedColor );
template<typename Adapter, typename V>
int PreprocessZoneLevel( const TimelineContext& ctx, const V& vec, int depth, bool visible, const uint32_t inheritedColor );
void PreprocessContextSwitches( const TimelineContext& ctx, const ContextSwitch& ctxSwitch, bool visible );
void PreprocessSamples( const TimelineContext& ctx, const Vector<SampleData>& vec, bool visible, int yPos );
void PreprocessMessages( const TimelineContext& ctx, const Vector<short_ptr<MessageData>>& vec, uint64_t tid, bool visible, int yPos );
void PreprocessLocks( const TimelineContext& ctx, const unordered_flat_map<uint32_t, LockMap*>& locks, uint32_t tid, TaskDispatch& td, bool visible );
const ThreadData* m_thread;
bool m_ghost;
std::vector<SamplesDraw> m_samplesDraw;
std::vector<ContextSwitchDraw> m_ctxDraw;
std::vector<TimelineDraw> m_draw;
std::vector<MessagesDraw> m_msgDraw;
std::vector<std::unique_ptr<LockDraw>> m_lockDraw;
int m_depth;
bool m_hasCtxSwitch;
bool m_hasSamples;
bool m_hasMessages;
};
}
#endif

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