Compare commits

...

40 Commits

Author SHA1 Message Date
Bartosz Taudul
dd29819f3d Merge pull request #1472 from alandtse/fix/emscripten-deploy-fork-guard
ci(emscripten): skip SFTP deploy on forks
2026-09-15 13:04:37 +02:00
Alan Tse
2ba0e21c97 ci(emscripten): skip SFTP deploy on forks
The deploy job SFTPs the emscripten build using repo secrets
(SERVER/USERNAME/PRIVATE_KEY/REMOTE_PATH) that only exist upstream.
On a fork these are empty, so every push to a fork's master fails
with "remote_path is empty" even though the actual build succeeded.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVBiYgtsB6okBueTwp5swa
2026-09-15 02:38:43 -07:00
Bartosz Taudul
a6d16f5515 Move LockHighlight to the timeline draw header. 2026-09-14 21:02:00 +02:00
Bartosz Taudul
e58f4819ba Merge pull request #1470 from wolfpld/slomp/webgpu-capacity
Increased the max query limit of TracyWebGPU.hpp
2026-09-14 19:33:05 +02:00
Marcos Slomp
841fae2371 code review: formating consistency 2026-09-14 10:16:24 -07:00
Marcos Slomp
016ae062e9 addressing review comments 2026-09-14 08:48:23 -07:00
Marcos Slomp
8cbf2b14a2 use FastVector for the shadow buffer 2026-09-14 08:48:23 -07:00
Marcos Slomp
6be14cff4e increasing the total query limit to 64K queries 2026-09-14 08:48:23 -07:00
Marcos Slomp
a11c8ab40c refactoring (isolating) Tracy protocol bits 2026-09-14 08:48:23 -07:00
Bartosz Taudul
c71e059d90 Bump exmples/dyna CPM to 0.43.1. 2026-09-13 20:30:40 +02:00
Bartosz Taudul
3247f490f7 Update NEWS. 2026-09-13 16:53:42 +02:00
Bartosz Taudul
edc649cdec Bind Ctrl+plus / Ctrl+minus / Ctrl+0 to the user interface scale. 2026-09-13 16:53:42 +02:00
Bartosz Taudul
1381f40bd9 Represent the user interface scale as a preset table index.
Replace the userScale float with a zoomLevel index into s_zoomPresets;
the ini key changes accordingly and previously saved floats are ignored.
2026-09-13 16:21:05 +02:00
Bartosz Taudul
ee067d112b Update NEWS. 2026-09-12 22:31:34 +02:00
Bartosz Taudul
0c35226446 Add warning about large traces. 2026-09-12 22:25:01 +02:00
Bartosz Taudul
63ffadc852 Load the startup trace in a background thread.
Opening and parsing on the browser main thread froze the tab for the
whole load; the file selector path already loads in a worker thread,
so boot now uses the same thread, progress modal, and error dialogs.
2026-09-12 22:19:59 +02:00
Bartosz Taudul
af3432cfde Fetch the startup trace instead of preloading the embedded one.
The preloaded data package made every ?t= load download embed.tracy
as well. The startup glue now fetches embed.tracy only when no URL is
given, into the same /url.tracy slot, and the boot code has a single
open path.
2026-09-12 22:19:59 +02:00
Bartosz Taudul
1fb370f935 Give version and load failure exceptions meaningful messages. 2026-09-12 22:19:59 +02:00
Bartosz Taudul
27f12a4eb5 Report unloadable trace files on the command line instead of crashing.
Version and integrity checks throw from the View constructor, which
was outside the try/catch guarding the file open, so a bad argv trace
terminated the process. Catch and report it there as well.
2026-09-12 22:19:58 +02:00
Bartosz Taudul
1c50138f85 Report failed second-trace loads in compare instead of crashing.
The compare load thread caught only UnsupportedVersion, so a
corrupted or otherwise unloadable second trace escaped the thread as
std::terminate. Catch the same set as the main trace load and route
it to the existing bad-version dialog.
2026-09-12 22:19:58 +02:00
Bartosz Taudul
2191885f86 Show a generic message for unreadable or corrupted trace files.
FileReadError now also signals truncated or malformed traces, not just
mapping failures, so the mmap-specific text misled users.
2026-09-12 22:19:58 +02:00
Bartosz Taudul
8a5f2c6f8c Show error dialogs instead of aborting the page on trace load errors.
Exception catching was off by default in the web build, so the
try/catch handlers around trace loading were dead code and any
failure unwound through the page.
2026-09-12 22:19:58 +02:00
Bartosz Taudul
643778c8e6 Decompress overflow streams inline on emscripten.
Loading on the browser main thread cannot start more than the 8
pre-created workers: beyond that, thread creation waits for the main
thread to return to its event loop, which the load never does.
Decompression threads were only an optimization, so on emscripten
spawn workers for at most 4 streams and decompress the rest on the
loading thread.
2026-09-12 22:19:58 +02:00
Bartosz Taudul
b45e9ee28a Do not truncate CallstackFrameId hash.
Frame address typically will be 64 bit, but size_t is 32-bit on emscripten.
2026-09-12 22:19:55 +02:00
Bartosz Taudul
fdd01d3007 Update manual. 2026-09-12 13:21:30 +02:00
Bartosz Taudul
8107754a3c Support loading trace files from URL in web version. 2026-09-12 13:08:04 +02:00
Bartosz Taudul
4332b43c3b Serve the web development files with CORS enabled.
The server doubles as a host for trace files to be loaded by the web
version from another origin, for example when profiling over the
local network.
2026-09-12 12:46:11 +02:00
Bartosz Taudul
0d6916273b Remove the trace filename from window title in web version.
The filename in the web build is an internal temporary name, either
embed.tracy or upload.tracy, and carries no information for the user.
2026-09-12 12:45:56 +02:00
Bartosz Taudul
e8a6be173f Ensure minimum find zone window width. 2026-09-11 22:34:04 +02:00
Bartosz Taudul
1c10623c6a Sync the manual with the display icon for GPU zones. 2026-09-11 22:23:43 +02:00
Bartosz Taudul
cf31c34367 Update NEWS. 2026-09-11 22:08:49 +02:00
Bartosz Taudul
7370f3a6b6 Prefix GPU zone names in the find list with the display icon. 2026-09-11 22:08:37 +02:00
Bartosz Taudul
6217872124 Explicit commit message rules. 2026-09-11 22:08:37 +02:00
Bartosz Taudul
4f0edd1304 Use the display icon for GPU zones. 2026-09-11 22:08:35 +02:00
Bartosz Taudul
ec4e36074b Merge pull request #1469 from wolfpld/slomp/gpu-stats
GPU Zone Statistics
2026-09-11 20:42:41 +02:00
Bartosz Taudul
70fe1e8d69 Use emscripten 6.0.9. 2026-09-11 19:51:07 +02:00
Bartosz Taudul
35b4922ed4 Bump LZ4 to 1.10.0. 2026-09-11 00:47:22 +02:00
Bartosz Taudul
8cf122cf22 Bump md4c to 00788b157. 2026-09-11 00:47:22 +02:00
Bartosz Taudul
fb1b13a8a8 Bump nfd to 7bbbd9fe. 2026-09-11 00:47:22 +02:00
Bartosz Taudul
159567701c Enable OBJC language on Apple builds.
glfw 3.5.1 enables OBJC build-wide under CMake 4.x, remapping nfd's
nfd_cocoa.m to a language its scope never loaded; generate then fails on
CMAKE_OBJC_COMPILE_OBJECT. Initialize OBJC at the top level so sibling
scopes get the rules.
2026-09-11 00:47:18 +02:00
31 changed files with 1791 additions and 776 deletions

View File

@@ -28,7 +28,7 @@ jobs:
- name: Setup emscripten - name: Setup emscripten
uses: emscripten-core/setup-emsdk@v16 uses: emscripten-core/setup-emsdk@v16
with: with:
version: 5.0.7 version: 6.0.9
- name: Trust git repo - name: Trust git repo
run: git config --global --add safe.directory '*' run: git config --global --add safe.directory '*'
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -57,7 +57,7 @@ jobs:
mkdir -p bin mkdir -p bin
cp profiler/build/index.html bin cp profiler/build/index.html bin
cp profiler/build/favicon.svg bin cp profiler/build/favicon.svg bin
cp profiler/build/tracy-profiler.data bin cp profiler/build/embed.tracy bin
cp profiler/build/tracy-profiler.js.gz bin cp profiler/build/tracy-profiler.js.gz bin
cp profiler/build/tracy-profiler.js.zst bin cp profiler/build/tracy-profiler.js.zst bin
cp profiler/build/tracy-profiler.wasm.gz bin cp profiler/build/tracy-profiler.wasm.gz bin
@@ -69,7 +69,7 @@ jobs:
deploy: deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build-emscripten needs: build-emscripten
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master' && github.repository == 'wolfpld/tracy'
steps: steps:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
- uses: wlixcc/SFTP-Deploy-Action@v1.2.4 - uses: wlixcc/SFTP-Deploy-Action@v1.2.4

View File

@@ -20,7 +20,8 @@
## Commits ## Commits
- Match the repository's established commit style; observe recent history first. - Subject: one line, imperative, capitalized, ends with a period — like `Bump LZ4 to 1.10.0.`, `Use the system wayland-protocols package.`.
- Body: only genuine rationale, mechanism, or constraints; skip it when the change is self-evident from the diff. Never recap decision processes or alternatives considered.
- One logical change per commit. Never bundle unrelated edits. - One logical change per commit. Never bundle unrelated edits.
- The rationale, mechanism, non-obvious constraints — belong in the commit message, not in code comments. - The rationale, mechanism, non-obvious constraints — belong in the commit message, not in code comments.
- Explain a pattern once, at its first occurrence; repeat occurrences at other sites state only what they do. - Explain a pattern once, at its first occurrence; repeat occurrences at other sites state only what they do.

26
NEWS
View File

@@ -5,7 +5,31 @@ here.
vx.xx.x (2026-xx-xx) vx.xx.x (2026-xx-xx)
-------------------- --------------------
- Find zone window now works with GPU zones (histogram, statistics, self time). - Added the monitor utility. This Linux-specific tool can launch or attach
to a program that has no Tracy instrumentation and perform a profiling run
based on sampling call stacks.
- It is now possible to load traces available on the web in the web demo
of the profiler. See the manual for details.
- Pandoc is now required to build the graphical profiler. The build system
will automatically download the tool. The pandoc binary will be stored
in the user cache directory.
- The profiler listening port can now be retrieved during runtime via the
TracyPort macro.
- The capture daemon will now lock the directory it writes to, so that two
simultaneously running daemons will not stomp on each other's data.
- Section rows on the timeline can now be grouped by their category.
- System tracing on Linux now properly parses rare non-default kernel data
structure formats.
- Removed filtering of context switch data when saving a trace to disk, as
it was losing the thread wake graph data.
- Fixed standard deviation in find zone window when the self or running time
option was enabled.
- Find zone window now works with GPU zones (histogram, statistics, self
time).
- Changed the GPU zones icon from an eye to a display monitor.
- User GUI scale in the profiler can be now controlled with Ctrl + plus/minus
and reset with Ctrl + zero.
- Various hardening improvements for correctness across the code base.
v0.14.1 (2026-08-22) v0.14.1 (2026-08-22)
-------------------- --------------------

View File

@@ -231,7 +231,7 @@ if(VENDOR_GUI)
CPMAddPackage( CPMAddPackage(
NAME nfd NAME nfd
GITHUB_REPOSITORY btzy/nativefiledialog-extended GITHUB_REPOSITORY btzy/nativefiledialog-extended
GIT_TAG 3cd252a8f7ca32419b1ca235c2990ba6a0ecba7c GIT_TAG 7bbbd9fe6b1d1549b41df138f614d1a44df9ba08
EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_ALL TRUE
PATCHES PATCHES
"${CMAKE_CURRENT_LIST_DIR}/nfd-xdg-foreign-v2.patch" "${CMAKE_CURRENT_LIST_DIR}/nfd-xdg-foreign-v2.patch"
@@ -246,7 +246,7 @@ if(VENDOR_GUI)
CPMAddPackage( CPMAddPackage(
NAME md4c NAME md4c
GITHUB_REPOSITORY mity/md4c GITHUB_REPOSITORY mity/md4c
GIT_TAG 65c6c9d72cebd9a731aaa5597414ce04d9ea5de3 GIT_TAG 00788b157b2b6ef3efd8659b66f6521229f382d7
OPTIONS OPTIONS
"BUILD_SHARED_LIBS OFF" "BUILD_SHARED_LIBS OFF"
EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_ALL TRUE

View File

@@ -2,8 +2,8 @@
# #
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors # SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
set(CPM_DOWNLOAD_VERSION 0.42.3) set(CPM_DOWNLOAD_VERSION 0.43.1)
set(CPM_HASH_SUM "a609e875fd532b067174250f6abbc3dac22fe2d64869783fb1e80bda1625c844") set(CPM_HASH_SUM "1c40fc102ce9625d7de7eb14f541cab30cc3138dca627f0b0ec40293ce6c2934")
if(CPM_SOURCE_CACHE) if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")

View File

@@ -3511,6 +3511,22 @@ Suppose the profiled application opted to provide trace parameters (see section~
You can pass the trace file name as an argument to the profiler application to open the capture, skipping the welcome dialog. You can also use the \texttt{-a address} argument to connect to the given address automatically. Finally, to specify the network port, pass the \texttt{-p port} parameter. The profiler will use it for client connections (overridable in the UI) and for listening to client discovery broadcasts. You can pass the trace file name as an argument to the profiler application to open the capture, skipping the welcome dialog. You can also use the \texttt{-a address} argument to connect to the given address automatically. Finally, to specify the network port, pass the \texttt{-p port} parameter. The profiler will use it for client connections (overridable in the UI) and for listening to client discovery broadcasts.
\subsubsection{Loading a trace from a URL}
The web version can load a trace directly from a URL passed in the \texttt{t} query parameter. The URL should point to the trace file, as shown below. URLs containing special characters such as \enquote{\texttt{\#}} or \enquote{\texttt{\&}} may need to be manually percent-encoded.
\texttt{https://tracy.nereid.pl/?t=https://example.com/capture.tracy}
The web server where the trace is stored must allow requests from the profiler's origin by sending the \texttt{Access-Control-Allow-Origin} header (e.g.,~\enquote{\texttt{*}}). Traces on the public internet must be served over HTTPS, while traces on the local network can be loaded by the public instance over plain HTTP as well. The first time this is attempted, the browser may ask for permission to access the local network.
\begin{bclogo}[
noborder=true,
couleur=black!5,
logo=\bcattention
]{Browser limits}
Note that attempting to load large traces in the browser will likely fail, as the web environment has limited resources.
\end{bclogo}
\subsection{Connection speed} \subsection{Connection speed}
Tracy network bandwidth requirements depend on the amount of data collection the profiled application performs. You may expect anything between 1~Mbps and 100~Mbps data transfer rate in typical use case scenarios. Tracy network bandwidth requirements depend on the amount of data collection the profiled application performs. You may expect anything between 1~Mbps and 100~Mbps data transfer rate in typical use case scenarios.
@@ -3811,7 +3827,7 @@ The control menu (top row of buttons) provides access to various profiler featur
\item \emph{\faImages{}~Frame statistics} -- Display frame statistics window (section~\ref{framestatistics}). \item \emph{\faImages{}~Frame statistics} -- Display frame statistics window (section~\ref{framestatistics}).
\end{itemize} \end{itemize}
\item \emph{ \faBook{}~User manual} -- Opens the user manual for quick reference. Note that the version of the user manual available directly in the profiler is an inferior quality version compared to the proper PDF. \item \emph{ \faBook{}~User manual} -- Opens the user manual for quick reference. Note that the version of the user manual available directly in the profiler is an inferior quality version compared to the proper PDF.
\item \emph{\faMagnifyingGlassPlus{}~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{\faMagnifyingGlassPlus{}~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. The user interface scale can also be adjusted with \keys{\ctrl + {+}} and \keys{\ctrl + \textminus}, or reset to 100\% with \keys{\ctrl + 0}.
\item \emph{\faRobot{}~Tracy Assist} -- Shows the automated assistant chat window (section~\ref{tracyassist}). Only available if enabled in global settings (section~\ref{aboutwindow}). \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} \end{itemize}
@@ -3829,7 +3845,7 @@ If the drawing of timeline elements was disabled in the options menu (section~\r
\item \faExpand{} -- Display of empty labels is enabled. \item \faExpand{} -- Display of empty labels is enabled.
\item \faPersonHiking{} -- Context switches are hidden. \item \faPersonHiking{} -- Context switches are hidden.
\item \faSliders{} -- CPU data is hidden. \item \faSliders{} -- CPU data is hidden.
\item \faEye{} -- GPU zones are hidden. \item \faDisplay{} -- GPU zones are hidden.
\item \faMicrochip{} -- CPU zones are hidden. \item \faMicrochip{} -- CPU zones are hidden.
\item \faLock{} -- Locks are hidden. \item \faLock{} -- Locks are hidden.
\item \faSignature{} -- Plots are hidden. \item \faSignature{} -- Plots are hidden.
@@ -4346,7 +4362,7 @@ In this window, you can set various trace-related options. For example, the time
\end{itemize} \end{itemize}
\item \emph{\faEyeDropper{}~Draw stack samples} -- Controls if stack samples for each thread are displayed on the timeline. \item \emph{\faEyeDropper{}~Draw stack samples} -- Controls if stack samples for each thread are displayed on the timeline.
\item \emph{\faArrowsLeftRightToLine{}~Draw sections} -- Allows disabling display of sections (see chapter~\ref{sections}). If there are multiple section categories, there's also an expandable list of categories that can be disabled or enabled. The \emph{\faLayerGroup{}~Group by category} option lays out each category in its own set of rows, ordered by category identifier, instead of packing all sections together by length. \item \emph{\faArrowsLeftRightToLine{}~Draw sections} -- Allows disabling display of sections (see chapter~\ref{sections}). If there are multiple section categories, there's also an expandable list of categories that can be disabled or enabled. The \emph{\faLayerGroup{}~Group by category} option lays out each category in its own set of rows, ordered by category identifier, instead of packing all sections together by length.
\item \emph{\faEye{} Draw GPU zones} -- Allows disabling display of OpenGL / Vulkan / Metal / Direct3D / OpenCL / CUDA / WebGPU zones. The \emph{GPU zones} drop-down allows disabling individual GPU contexts and setting CPU/GPU drift offsets of uncalibrated contexts (see section~\ref{gpuprofiling} for more information). The \emph{\faRobot~Auto} button automatically measures the GPU drift value\footnote{There is an assumption that drift is linear. Automated measurement calculates and removes change over time in delay-to-execution of GPU zones. Resulting value may still be incorrect.}. \item \emph{\faDisplay{} Draw GPU zones} -- Allows disabling display of OpenGL / Vulkan / Metal / Direct3D / OpenCL / CUDA / WebGPU zones. The \emph{GPU zones} drop-down allows disabling individual GPU contexts and setting CPU/GPU drift offsets of uncalibrated contexts (see section~\ref{gpuprofiling} for more information). The \emph{\faRobot~Auto} button automatically measures the GPU drift value\footnote{There is an assumption that drift is linear. Automated measurement calculates and removes change over time in delay-to-execution of GPU zones. Resulting value may still be incorrect.}.
\item \emph{\faMicrochip{} Draw CPU zones} -- Determines whether CPU zones are displayed. \item \emph{\faMicrochip{} Draw CPU zones} -- Determines whether CPU zones are displayed.
\begin{itemize} \begin{itemize}
\item \emph{\faGhost{} Draw ghost zones} -- Controls if ghost zones should be displayed in threads which don't have any instrumented zones available. \item \emph{\faGhost{} Draw ghost zones} -- Controls if ghost zones should be displayed in threads which don't have any instrumented zones available.
@@ -4408,7 +4424,7 @@ Looking at the timeline view gives you a very localized outlook on things. Howev
If the trace capture was performed with call stack sampling enabled (as described in chapter~\ref{sampling}), you will be presented with an option to switch between \emph{\faSyringe{}~Instrumentation} and \emph{\faEyeDropper{}~Sampling} modes. If the profiler collected no sampling data, but it retrieved symbols, the second mode will be displayed as \emph{\faPuzzlePiece{}~Symbols}, enabling you to list available symbols. If the trace capture was performed with call stack sampling enabled (as described in chapter~\ref{sampling}), you will be presented with an option to switch between \emph{\faSyringe{}~Instrumentation} and \emph{\faEyeDropper{}~Sampling} modes. If the profiler collected no sampling data, but it retrieved symbols, the second mode will be displayed as \emph{\faPuzzlePiece{}~Symbols}, enabling you to list available symbols.
If GPU zones were captured, you would also have the \emph{\faEye{}~GPU} option to view the GPU zones statistics. If GPU zones were captured, you would also have the \emph{\faDisplay{}~GPU} option to view the GPU zones statistics.
\subsubsection{Instrumentation mode} \subsubsection{Instrumentation mode}
@@ -4474,7 +4490,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. 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.
GPU zones are supported as well, using their GPU execution time. Zone grouping, the found-zones list, and call stack samples are available only for CPU zones. GPU zones are supported as well, using their GPU execution time. GPU zones are prefixed with the~\faDisplay~icon. Zone grouping, the found-zones list, and call stack samples are available only for CPU zones.
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.

View File

@@ -25,6 +25,10 @@ project(
VERSION ${TRACY_VERSION_STRING} VERSION ${TRACY_VERSION_STRING}
) )
if(APPLE)
enable_language(OBJC)
endif()
if(SELF_PROFILE) if(SELF_PROFILE)
add_definitions(-DTRACY_ENABLE) add_definitions(-DTRACY_ENABLE)
add_compile_options(-g -O3 -fno-omit-frame-pointer) add_compile_options(-g -O3 -fno-omit-frame-pointer)
@@ -354,6 +358,7 @@ if(NOT EMSCRIPTEN)
endif() endif()
if(EMSCRIPTEN) if(EMSCRIPTEN)
target_compile_options(${PROJECT_NAME} PRIVATE -sDISABLE_EXCEPTION_CATCHING=0)
target_link_options(${PROJECT_NAME} PRIVATE target_link_options(${PROJECT_NAME} PRIVATE
-pthread -pthread
-sASSERTIONS=0 -sASSERTIONS=0
@@ -365,7 +370,8 @@ if(EMSCRIPTEN)
-sEXPORTED_FUNCTIONS=_main,_nativeOpenFile,_tracy_paste_clipboard -sEXPORTED_FUNCTIONS=_main,_nativeOpenFile,_tracy_paste_clipboard
-sEXPORTED_RUNTIME_METHODS=ccall -sEXPORTED_RUNTIME_METHODS=ccall
-sENVIRONMENT=web,worker -sENVIRONMENT=web,worker
--preload-file embed.tracy -sDISABLE_EXCEPTION_CATCHING=0
"--pre-js;${CMAKE_CURRENT_LIST_DIR}/wasm/url_trace_pre.js"
) )
file(DOWNLOAD https://share.nereid.pl/i/embed.tracy ${CMAKE_CURRENT_BINARY_DIR}/embed.tracy EXPECTED_MD5 f018919fcd34de2fb349be8be354048b) file(DOWNLOAD https://share.nereid.pl/i/embed.tracy ${CMAKE_CURRENT_BINARY_DIR}/embed.tracy EXPECTED_MD5 f018919fcd34de2fb349be8be354048b)

View File

@@ -20,6 +20,10 @@
# include <windows.h> # include <windows.h>
#endif #endif
#ifdef __EMSCRIPTEN__
# include <emscripten.h>
#endif
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG #define STBI_ONLY_PNG
#include "stb_image.h" #include "stb_image.h"
@@ -165,10 +169,10 @@ static void ScaleWindow(ImGuiWindow* window, float scale)
static void SetupDPIScale() static void SetupDPIScale()
{ {
auto scale = dpiScale * tracy::s_config.userScale; auto scale = dpiScale * tracy::s_zoomPresets[tracy::s_config.zoomLevel];
#ifdef __APPLE__ #ifdef __APPLE__
scale = tracy::s_config.userScale; scale = tracy::s_zoomPresets[tracy::s_config.zoomLevel];
#endif #endif
if( !dpiFirstSetup && prevScale == scale ) return; if( !dpiFirstSetup && prevScale == scale ) return;
@@ -214,9 +218,10 @@ static int IsBusy()
return 0; return 0;
} }
static void SetupScaleCallback( float scale ) static void SetupScaleCallback( int preset )
{ {
tracy::s_config.userScale = scale; assert( preset >= 0 && preset < tracy::s_zoomPresetCount );
tracy::s_config.zoomLevel = preset;
if ( tracy::s_config.saveUserScale ) tracy::SaveConfig(); if ( tracy::s_config.saveUserScale ) tracy::SaveConfig();
RunOnMainThread( []{ SetupDPIScale(); }, true ); RunOnMainThread( []{ SetupDPIScale(); }, true );
} }
@@ -236,7 +241,30 @@ int main( int argc, char** argv )
std::unique_ptr<tracy::FileRead> initFileOpen; std::unique_ptr<tracy::FileRead> initFileOpen;
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
initFileOpen = std::unique_ptr<tracy::FileRead>( tracy::FileRead::Open( "embed.tracy" ) ); const auto FileExists = []( const char* fn ) -> bool
{
auto f = fopen( fn, "rb" );
if( f ) fclose( f );
return f != nullptr;
};
if( FileExists( "/url.tracy" ) )
{
loadThread = std::thread( [] {
try
{
auto f = std::shared_ptr<tracy::FileRead>( tracy::FileRead::Open( "/url.tracy" ) );
if( f )
{
view.store( std::make_shared<tracy::View>( RunOnMainThread, *f, SetWindowTitleCallback, SetupScaleCallback, AttentionCallback, s_achievements ), std::memory_order_release );
}
}
catch( const tracy::NotTracyDump& ) { badVer.state = tracy::BadVersionState::BadFile; }
catch( const tracy::FileReadError& ) { badVer.state = tracy::BadVersionState::ReadError; }
catch( const tracy::UnsupportedVersion& e ) { badVer.state = tracy::BadVersionState::UnsupportedVersion; badVer.version = e.version; }
catch( const tracy::LegacyVersion& e ) { badVer.state = tracy::BadVersionState::LegacyVersion; badVer.version = e.version; }
catch( const tracy::LoadFailure& e ) { badVer.state = tracy::BadVersionState::LoadFailure; badVer.msg = e.msg; }
} );
}
#endif #endif
if( argc == 2 ) if( argc == 2 )
{ {
@@ -364,7 +392,16 @@ int main( int argc, char** argv )
if( initFileOpen ) if( initFileOpen )
{ {
view.store( std::make_shared<tracy::View>( RunOnMainThread, *initFileOpen, SetWindowTitleCallback, SetupScaleCallback, AttentionCallback, s_achievements ), std::memory_order_release ); try
{
view.store( std::make_shared<tracy::View>( RunOnMainThread, *initFileOpen, SetWindowTitleCallback, SetupScaleCallback, AttentionCallback, s_achievements ), std::memory_order_release );
}
catch( const std::exception& e )
{
fprintf( stderr, "Cannot load trace file: %s\n", e.what() );
initFileOpen.reset();
_Exit( 1 );
}
initFileOpen.reset(); initFileOpen.reset();
} }
else if( connectTo ) else if( connectTo )

View File

@@ -58,7 +58,7 @@ void BadVersionImpl( BadVersionState& badVer )
ImGui::PushFont( g_fonts.normal, FontBig ); ImGui::PushFont( g_fonts.normal, FontBig );
TextCentered( ICON_FA_TRIANGLE_EXCLAMATION ); TextCentered( ICON_FA_TRIANGLE_EXCLAMATION );
ImGui::PopFont(); ImGui::PopFont();
ImGui::Text( "The file you are trying to open cannot be mapped to memory." ); ImGui::Text( "The file could not be read or is corrupted." );
ImGui::Separator(); ImGui::Separator();
if( ImGui::Button( "OK" ) ) if( ImGui::Button( "OK" ) )
{ {

View File

@@ -38,7 +38,7 @@ void LoadConfig()
if( ini_sget( ini, "achievements", "enabled", "%d", &v ) ) s_config.achievements = v; if( ini_sget( ini, "achievements", "enabled", "%d", &v ) ) s_config.achievements = v;
if( ini_sget( ini, "achievements", "asked", "%d", &v ) ) s_config.achievementsAsked = v; if( ini_sget( ini, "achievements", "asked", "%d", &v ) ) s_config.achievementsAsked = v;
if( ini_sget( ini, "ui", "saveUserScale", "%d", &v ) ) s_config.saveUserScale = v; if( ini_sget( ini, "ui", "saveUserScale", "%d", &v ) ) s_config.saveUserScale = v;
if( ini_sget( ini, "ui", "userScale", "%lf", &v1 ) && v1 > 0.0 && s_config.saveUserScale ) s_config.userScale = v1; if( ini_sget( ini, "ui", "zoomLevel", "%d", &v ) && v >= 0 && v < s_zoomPresetCount ) s_config.zoomLevel = v;
if( ini_sget( ini, "llm", "enabled", "%d", &v ) ) s_config.llm = v; if( ini_sget( ini, "llm", "enabled", "%d", &v ) ) s_config.llm = v;
if( v2 = ini_get( ini, "llm", "address" ); v2 ) s_config.llmAddress = v2; if( v2 = ini_get( ini, "llm", "address" ); v2 ) s_config.llmAddress = v2;
if( v2 = ini_get( ini, "llm", "model" ); v2 ) s_config.llmModel = v2; if( v2 = ini_get( ini, "llm", "model" ); v2 ) s_config.llmModel = v2;
@@ -92,7 +92,7 @@ bool SaveConfig()
fprintf( f, "\n[ui]\n" ); fprintf( f, "\n[ui]\n" );
fprintf( f, "saveUserScale = %i\n", (int)s_config.saveUserScale ); fprintf( f, "saveUserScale = %i\n", (int)s_config.saveUserScale );
fprintf( f, "userScale = %lf\n", s_config.userScale ); fprintf( f, "zoomLevel = %i\n", s_config.zoomLevel );
fprintf( f, "\n[llm]\n" ); fprintf( f, "\n[llm]\n" );
fprintf( f, "enabled = %i\n", (int)s_config.llm ); fprintf( f, "enabled = %i\n", (int)s_config.llm );

View File

@@ -8,6 +8,12 @@
namespace tracy namespace tracy
{ {
inline constexpr float s_zoomPresets[] = {
1.f/2, 1.f/1.75f, 1.f/1.5f, 1.f/1.25f, 1.f, 1.25f, 1.5f, 1.75f, 2.f, 2.25f, 2.5f, 2.75f, 3.f
};
constexpr int s_zoomPresetCount = sizeof( s_zoomPresets ) / sizeof( *s_zoomPresets );
constexpr int s_zoomPreset100 = 4;
struct Config struct Config
{ {
bool threadedRendering = true; bool threadedRendering = true;
@@ -28,7 +34,7 @@ struct Config
bool drawContextSwitches = true; bool drawContextSwitches = true;
int plotHeight = 100; int plotHeight = 100;
bool saveUserScale = false; bool saveUserScale = false;
float userScale = 1.0f; int zoomLevel = s_zoomPreset100;
// LLM assistant settings // LLM assistant settings
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__

View File

@@ -108,6 +108,15 @@ struct LockDraw
std::vector<LockDrawItem> data; std::vector<LockDrawItem> data;
}; };
struct LockHighlight
{
int64_t id;
int64_t begin;
int64_t end;
uint8_t thread;
bool blocked;
};
} }
#endif #endif

View File

@@ -163,6 +163,18 @@ void View::SaveUserData()
m_userData.Save(); m_userData.Save();
} }
void View::SetZoomPreset( int idx )
{
assert( idx >= 0 && idx < s_zoomPresetCount );
if( m_sscb ) m_sscb( idx );
}
void View::ZoomUserScale( int dir )
{
const int idx = dir == 0 ? s_zoomPreset100 : s_config.zoomLevel + dir;
if( idx >= 0 && idx < s_zoomPresetCount ) SetZoomPreset( idx );
}
void View::ViewSource( const char* fileName, int line ) void View::ViewSource( const char* fileName, int line )
{ {
assert( fileName ); assert( fileName );
@@ -787,6 +799,21 @@ bool View::DrawImpl()
m_findZone.show = true; m_findZone.show = true;
m_shortcut = ShortcutAction::OpenFind; m_shortcut = ShortcutAction::OpenFind;
} }
if( m_sscb )
{
if( ImGui::IsKeyPressed( ImGuiKey_Equal, false ) || ImGui::IsKeyPressed( ImGuiKey_KeypadAdd, false ) )
{
ZoomUserScale( 1 );
}
else if( ImGui::IsKeyPressed( ImGuiKey_Minus, false ) || ImGui::IsKeyPressed( ImGuiKey_KeypadSubtract, false ) )
{
ZoomUserScale( -1 );
}
else if( ImGui::IsKeyPressed( ImGuiKey_0, false ) || ImGui::IsKeyPressed( ImGuiKey_Keypad0, false ) )
{
ZoomUserScale( 0 );
}
}
} }
if( !m_frames ) m_frames = m_worker.GetFramesBase(); if( !m_frames ) m_frames = m_worker.GetFramesBase();
@@ -995,19 +1022,12 @@ bool View::DrawImpl()
if( ImGui::Button( ICON_FA_MAGNIFYING_GLASS_PLUS ) ) ImGui::OpenPopup( "ZoomPopup" ); if( ImGui::Button( ICON_FA_MAGNIFYING_GLASS_PLUS ) ) ImGui::OpenPopup( "ZoomPopup" );
if( ImGui::BeginPopup( "ZoomPopup" ) ) if( ImGui::BeginPopup( "ZoomPopup" ) )
{ {
if( ImGui::Button( "50%" ) ) m_sscb( 1.f/2 ); for( int i=0; i<s_zoomPresetCount; i++ )
if( ImGui::Button( "57%" ) ) m_sscb( 1.f/1.75f ); {
if( ImGui::Button( "66%" ) ) m_sscb( 1.f/1.5f ); char label[32];
if( ImGui::Button( "80%" ) ) m_sscb( 1.f/1.25f ); sprintf( label, "%i%%", int( s_zoomPresets[i] * 100.f + 0.001f ) );
if( ImGui::Button( "100%" ) ) m_sscb( 1.f ); if( ImGui::RadioButton( label, i == s_config.zoomLevel ) ) SetZoomPreset( i );
if( ImGui::Button( "125%" ) ) m_sscb( 1.25f ); }
if( ImGui::Button( "150%" ) ) m_sscb( 1.5f );
if( ImGui::Button( "175%" ) ) m_sscb( 1.75f );
if( ImGui::Button( "200%" ) ) m_sscb( 2.f );
if( ImGui::Button( "225%" ) ) m_sscb( 2.25f );
if( ImGui::Button( "250%" ) ) m_sscb( 2.5f );
if( ImGui::Button( "275%" ) ) m_sscb( 2.75f );
if( ImGui::Button( "300%" ) ) m_sscb( 3.f );
ImGui::EndPopup(); ImGui::EndPopup();
} }
} }

View File

@@ -22,6 +22,7 @@
#include "TracyMarkdown.hpp" #include "TracyMarkdown.hpp"
#include "TracySourceContents.hpp" #include "TracySourceContents.hpp"
#include "TracyTimelineController.hpp" #include "TracyTimelineController.hpp"
#include "TracyTimelineDraw.hpp"
#include "TracyUserData.hpp" #include "TracyUserData.hpp"
#include "TracyUtility.hpp" #include "TracyUtility.hpp"
#include "TracyViewData.hpp" #include "TracyViewData.hpp"
@@ -145,7 +146,7 @@ public:
}; };
using SetTitleCallback = void(*)( const char* ); using SetTitleCallback = void(*)( const char* );
using SetScaleCallback = void(*)( float ); using SetScaleCallback = void(*)( int );
using AttentionCallback = void(*)(); using AttentionCallback = void(*)();
View( void(*cbMainThread)(const std::function<void()>&, bool), const char* addr, uint16_t port, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, AchievementsMgr* amgr ); View( void(*cbMainThread)(const std::function<void()>&, bool), const char* addr, uint16_t port, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, AchievementsMgr* amgr );
@@ -304,6 +305,8 @@ private:
void SetupRanges(); void SetupRanges();
void Achieve( const char* id ); void Achieve( const char* id );
void SaveUserData(); void SaveUserData();
void SetZoomPreset( int idx );
void ZoomUserScale( int dir );
bool DrawImpl(); bool DrawImpl();
void DrawFrameImage( FrameImageCache& cache, const FrameImage& fi, float scale = GetScale() ); void DrawFrameImage( FrameImageCache& cache, const FrameImage& fi, float scale = GetScale() );

View File

@@ -245,6 +245,20 @@ void View::DrawCompare()
m_compare.badVer.state = BadVersionState::UnsupportedVersion; m_compare.badVer.state = BadVersionState::UnsupportedVersion;
m_compare.badVer.version = e.version; m_compare.badVer.version = e.version;
} }
catch( const tracy::LegacyVersion& e )
{
m_compare.badVer.state = BadVersionState::LegacyVersion;
m_compare.badVer.version = e.version;
}
catch( const tracy::LoadFailure& e )
{
m_compare.badVer.state = BadVersionState::LoadFailure;
m_compare.badVer.msg = e.msg;
}
catch( const tracy::FileReadError& )
{
m_compare.badVer.state = BadVersionState::ReadError;
}
} ); } );
} }
} }

View File

@@ -405,7 +405,15 @@ void View::DrawFindZone()
ImGui::SameLine(); ImGui::SameLine();
ImGui::PushID( idx ); ImGui::PushID( idx );
ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) ); ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
ImGui::RadioButton( m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ), &m_findZone.selMatch, idx++ ); auto name = m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function );
std::string gpuLabel;
if( isGpuLoc )
{
gpuLabel = ICON_FA_DISPLAY " ";
gpuLabel += name;
name = gpuLabel.c_str();
}
ImGui::RadioButton( name, &m_findZone.selMatch, idx++ );
ImGui::PopStyleVar(); ImGui::PopStyleVar();
if( m_findZoneBuzzAnim.Match( idx ) ) if( m_findZoneBuzzAnim.Match( idx ) )
{ {
@@ -1678,6 +1686,7 @@ void View::DrawFindZone()
{ {
ImGui::Separator(); ImGui::Separator();
TextDisabledUnformatted( "Zone grouping, the found-zones list, and callstack samples are not yet available for GPU zones." ); TextDisabledUnformatted( "Zone grouping, the found-zones list, and callstack samples are not yet available for GPU zones." );
m_findZoneConstraint.MarkMinWidth();
} }
else else
{ {

View File

@@ -151,7 +151,7 @@ void View::DrawNotificationArea()
if( !m_vd.drawGpuZones ) if( !m_vd.drawGpuZones )
{ {
ImGui::SameLine(); ImGui::SameLine();
TextColoredUnformatted( ImVec4( 1, 0.5, 0, 1 ), ICON_FA_EYE ); TextColoredUnformatted( ImVec4( 1, 0.5, 0, 1 ), ICON_FA_DISPLAY );
if( ImGui::IsItemHovered() ) if( ImGui::IsItemHovered() )
{ {
ImGui::BeginTooltip(); ImGui::BeginTooltip();

View File

@@ -172,7 +172,7 @@ void View::DrawOptions()
{ {
ImGui::Separator(); ImGui::Separator();
val = m_vd.drawGpuZones; val = m_vd.drawGpuZones;
ImGui::Checkbox( ICON_FA_EYE " Draw GPU zones", &val ); ImGui::Checkbox( ICON_FA_DISPLAY " Draw GPU zones", &val );
m_vd.drawGpuZones = val; m_vd.drawGpuZones = val;
const auto expand = ImGui::TreeNode( "GPU zones" ); const auto expand = ImGui::TreeNode( "GPU zones" );
ImGui::SameLine(); ImGui::SameLine();

View File

@@ -80,7 +80,7 @@ void View::DrawStatistics()
ImGui::SameLine(); ImGui::SameLine();
ImGui::Spacing(); ImGui::Spacing();
ImGui::SameLine(); ImGui::SameLine();
ImGui::RadioButton( ICON_FA_EYE " GPU", &m_statMode, 2 ); ImGui::RadioButton( ICON_FA_DISPLAY " GPU", &m_statMode, 2 );
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::Spacing(); ImGui::Spacing();

View File

@@ -896,6 +896,7 @@ void View::UpdateTitle()
snprintf( buf, 1024, "%s (%s)", captureName, desc.c_str() ); snprintf( buf, 1024, "%s (%s)", captureName, desc.c_str() );
m_stcb( buf ); m_stcb( buf );
} }
#ifndef __EMSCRIPTEN__
else if( !m_filename.empty() ) else if( !m_filename.empty() )
{ {
auto fptr = m_filename.c_str() + m_filename.size() - 1; auto fptr = m_filename.c_str() + m_filename.size() - 1;
@@ -906,6 +907,7 @@ void View::UpdateTitle()
snprintf( buf, 1024, "%s (%s)", captureName, fptr ); snprintf( buf, 1024, "%s (%s)", captureName, fptr );
m_stcb( buf ); m_stcb( buf );
} }
#endif
else else
{ {
m_stcb( captureName ); m_stcb( captureName );

View File

@@ -9,6 +9,7 @@ class MyHTTPRequestHandler(server.SimpleHTTPRequestHandler):
self.send_header("Cache-Control", "no-cache, no-store, must-revalidate") self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
self.send_header("Pragma", "no-cache") self.send_header("Pragma", "no-cache")
self.send_header("Expires", "0") self.send_header("Expires", "0")
self.send_header("Access-Control-Allow-Origin", "*");
self.send_header("Cross-Origin-Embedder-Policy", "require-corp"); self.send_header("Cross-Origin-Embedder-Policy", "require-corp");
self.send_header("Cross-Origin-Opener-Policy", "same-origin"); self.send_header("Cross-Origin-Opener-Policy", "same-origin");

View File

@@ -0,0 +1,94 @@
// Startup trace loader: fetches ?t=<url>, or embed.tracy next to the page when no
// URL is given, into the /url.tracy virtual file.
if( typeof document !== 'undefined' )
{
const tracyTraceParam = new URLSearchParams( window.location.search ).get( 't' );
let tracyUrl = null;
let tracyUrlError = null;
if( tracyTraceParam )
{
try { tracyUrl = new URL( tracyTraceParam, window.location.href ); }
catch( e ) { tracyUrlError = 'invalid URL'; }
if( tracyUrl && ( ( tracyUrl.protocol !== 'https:' && tracyUrl.protocol !== 'http:' ) ||
tracyUrl.username !== '' || tracyUrl.password !== '' ) )
tracyUrlError = 'unsupported URL (https/http without embedded credentials required)';
}
else
{
tracyUrl = new URL( 'embed.tracy', window.location.href );
}
const isTracyFile = ( b ) => b.length >= 4 && (
( b[0] === 0x74 && b[1] === 0x72 && b[2] === 0xfd && b[3] === 0x50 ) ||
( b[0] === 0x74 && b[1] === 0x6c && b[2] === 0x5a && b[3] === 0x04 ) ||
( b[0] === 0x74 && b[1] === 0x5a && b[2] === 0x73 && b[3] === 0x74 ) );
const tracyTraceData = ( () =>
{
if( tracyUrlError ) return Promise.reject( tracyUrlError );
const opts = { mode: 'cors' };
if( tracyTraceParam && tracyUrl.protocol === 'http:' )
{
const loopback = tracyUrl.hostname === 'localhost' || tracyUrl.hostname.endsWith( '.localhost' ) ||
tracyUrl.hostname === '[::1]' || /^127\./.test( tracyUrl.hostname );
// Local Network Access: pre-resolve classification, relaxes mixed content for local targets.
opts.targetAddressSpace = loopback ? 'loopback' : 'local';
}
const ctrl = new AbortController();
opts.signal = ctrl.signal;
const timeout = setTimeout( () => ctrl.abort(), 300000 );
Module.setStatus( 'Loading trace...' );
return fetch( tracyUrl.href, opts ).then( ( resp ) =>
{
if( !resp.ok ) { clearTimeout( timeout ); throw 'HTTP status ' + resp.status; }
const total = Number( resp.headers.get( 'Content-Length' ) || 0 );
if( !resp.body || !total )
{
return resp.arrayBuffer().then(
( ab ) => { clearTimeout( timeout ); return new Uint8Array( ab ); },
( e ) => { clearTimeout( timeout ); throw e; } );
}
const reader = resp.body.getReader();
const chunks = [];
let received = 0;
const pump = () => reader.read().then( ( { done, value } ) =>
{
if( done )
{
clearTimeout( timeout );
const out = new Uint8Array( received );
let off = 0;
for( const c of chunks ) { out.set( c, off ); off += c.length; }
return out;
}
chunks.push( value );
received += value.length;
Module.setStatus( 'Loading trace (' + received + '/' + total + ')' );
return pump();
} );
return pump();
},
( e ) => { clearTimeout( timeout ); throw ( e && e.name === 'AbortError' ) ? 'download timed out' : 'fetch failed'; } );
} )();
Module.preRun = Module.preRun || [];
Module.preRun.push( () =>
{
addRunDependency( 'tracy-startup-trace' );
tracyTraceData.then(
( bytes ) =>
{
if( !isTracyFile( bytes ) )
{
FS.writeFile( '/url.tracy.failed', new Uint8Array( 0 ) );
alert( 'Cannot load trace from ' + tracyUrl.href + ': not a Tracy trace file' );
}
else FS.writeFile( '/url.tracy', bytes );
},
( err ) =>
{
FS.writeFile( '/url.tracy.failed', new Uint8Array( 0 ) );
alert( 'Cannot load trace from ' + tracyUrl.href + ': ' + err );
} ).finally( () => removeRunDependency( 'tracy-startup-trace' ) );
} );
}

View File

@@ -1,6 +1,6 @@
/* /*
LZ4 - Fast LZ compression algorithm LZ4 - Fast LZ compression algorithm
Copyright (C) 2011-2020, Yann Collet. Copyright (C) 2011-2023, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
@@ -37,7 +37,8 @@
**************************************/ **************************************/
/* /*
* LZ4_HEAPMODE : * LZ4_HEAPMODE :
* Select how default compression functions will allocate memory for their hash table, * Select how stateless compression functions like `LZ4_compress_default()`
* allocate memory for their hash table,
* in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
*/ */
#ifndef LZ4_HEAPMODE #ifndef LZ4_HEAPMODE
@@ -78,7 +79,7 @@
( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \
|| defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
# define LZ4_FORCE_MEMORY_ACCESS 2 # define LZ4_FORCE_MEMORY_ACCESS 2
# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) # elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) || defined(_MSC_VER)
# define LZ4_FORCE_MEMORY_ACCESS 1 # define LZ4_FORCE_MEMORY_ACCESS 1
# endif # endif
#endif #endif
@@ -105,15 +106,13 @@
# define LZ4_SRC_INCLUDED 1 # define LZ4_SRC_INCLUDED 1
#endif #endif
#ifndef LZ4_STATIC_LINKING_ONLY
#define LZ4_STATIC_LINKING_ONLY
#endif
#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS #ifndef LZ4_DISABLE_DEPRECATE_WARNINGS
#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ # define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */
#endif #endif
#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ #ifndef LZ4_STATIC_LINKING_ONLY
# define LZ4_STATIC_LINKING_ONLY
#endif
#include "tracy_lz4.hpp" #include "tracy_lz4.hpp"
/* see also "memory routines" below */ /* see also "memory routines" below */
@@ -125,6 +124,9 @@
# include <intrin.h> /* only present in VS2005+ */ # include <intrin.h> /* only present in VS2005+ */
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
# pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */ # pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */
# pragma warning(disable : 6239) /* disable: C6239: (<non-zero constant> && <expression>) always evaluates to the result of <expression> */
# pragma warning(disable : 6240) /* disable: C6240: (<expression> && <non-zero constant>) always evaluates to the result of <expression> */
# pragma warning(disable : 6326) /* disable: C6326: Potential comparison of a constant with another constant */
#endif /* _MSC_VER */ #endif /* _MSC_VER */
#ifndef LZ4_FORCE_INLINE #ifndef LZ4_FORCE_INLINE
@@ -279,7 +281,7 @@ static const int LZ4_minLength = (MFLIMIT+1);
static int g_debuglog_enable = 1; static int g_debuglog_enable = 1;
# define DEBUGLOG(l, ...) { \ # define DEBUGLOG(l, ...) { \
if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \
fprintf(stderr, __FILE__ ": "); \ fprintf(stderr, __FILE__ " %i: ", __LINE__); \
fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, " \n"); \ fprintf(stderr, " \n"); \
} } } }
@@ -366,6 +368,11 @@ static unsigned LZ4_isLittleEndian(void)
return one.c[0]; return one.c[0];
} }
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
#define LZ4_PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#elif defined(_MSC_VER)
#define LZ4_PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
#endif
#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) #if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
/* lie to the compiler about data alignment; use with caution */ /* lie to the compiler about data alignment; use with caution */
@@ -381,14 +388,16 @@ static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
/* currently only defined for gcc and icc */ /* currently only defined for gcc and icc */
typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign; LZ4_PACK(typedef struct { U16 u16; }) LZ4_unalign16;
LZ4_PACK(typedef struct { U32 u32; }) LZ4_unalign32;
LZ4_PACK(typedef struct { reg_t uArch; }) LZ4_unalignST;
static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; } static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign16*)ptr)->u16; }
static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; } static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign32*)ptr)->u32; }
static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; } static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalignST*)ptr)->uArch; }
static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; } static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign16*)memPtr)->u16 = value; }
static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; } static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign32*)memPtr)->u32 = value; }
#else /* safe and portable access using memcpy() */ #else /* safe and portable access using memcpy() */
@@ -426,10 +435,22 @@ static U16 LZ4_readLE16(const void* memPtr)
return LZ4_read16(memPtr); return LZ4_read16(memPtr);
} else { } else {
const BYTE* p = (const BYTE*)memPtr; const BYTE* p = (const BYTE*)memPtr;
return (U16)((U16)p[0] + (p[1]<<8)); return (U16)((U16)p[0] | (p[1]<<8));
} }
} }
#ifdef LZ4_STATIC_LINKING_ONLY_ENDIANNESS_INDEPENDENT_OUTPUT
static U32 LZ4_readLE32(const void* memPtr)
{
if (LZ4_isLittleEndian()) {
return LZ4_read32(memPtr);
} else {
const BYTE* p = (const BYTE*)memPtr;
return (U32)p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
}
}
#endif
static void LZ4_writeLE16(void* memPtr, U16 value) static void LZ4_writeLE16(void* memPtr, U16 value)
{ {
if (LZ4_isLittleEndian()) { if (LZ4_isLittleEndian()) {
@@ -511,7 +532,7 @@ LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)
/* LZ4_memcpy_using_offset() presumes : /* LZ4_memcpy_using_offset() presumes :
* - dstEnd >= dstPtr + MINMATCH * - dstEnd >= dstPtr + MINMATCH
* - there is at least 8 bytes available to write after dstEnd */ * - there is at least 12 bytes available to write after dstEnd */
LZ4_FORCE_INLINE void LZ4_FORCE_INLINE void
LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)
{ {
@@ -526,12 +547,12 @@ LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const si
case 2: case 2:
LZ4_memcpy(v, srcPtr, 2); LZ4_memcpy(v, srcPtr, 2);
LZ4_memcpy(&v[2], srcPtr, 2); LZ4_memcpy(&v[2], srcPtr, 2);
#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ #if defined(_MSC_VER) && (_MSC_VER <= 1937) /* MSVC 2022 ver 17.7 or earlier */
# pragma warning(push) # pragma warning(push)
# pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */ # pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */
#endif #endif
LZ4_memcpy(&v[4], v, 4); LZ4_memcpy(&v[4], v, 4);
#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ #if defined(_MSC_VER) && (_MSC_VER <= 1937) /* MSVC 2022 ver 17.7 or earlier */
# pragma warning(pop) # pragma warning(pop)
#endif #endif
break; break;
@@ -772,7 +793,12 @@ LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
{ {
if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
#ifdef LZ4_STATIC_LINKING_ONLY_ENDIANNESS_INDEPENDENT_OUTPUT
return LZ4_hash4(LZ4_readLE32(p), tableType);
#else
return LZ4_hash4(LZ4_read32(p), tableType); return LZ4_hash4(LZ4_read32(p), tableType);
#endif
} }
LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)
@@ -799,23 +825,19 @@ LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableT
} }
} }
/* LZ4_putPosition*() : only used in byPtr mode */
LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h,
void* tableBase, tableType_t const tableType, void* tableBase, tableType_t const tableType)
const BYTE* srcBase)
{ {
switch (tableType) const BYTE** const hashTable = (const BYTE**)tableBase;
{ assert(tableType == byPtr); (void)tableType;
case clearedTable: { /* illegal! */ assert(0); return; } hashTable[h] = p;
case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
}
} }
LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType)
{ {
U32 const h = LZ4_hashPosition(p, tableType); U32 const h = LZ4_hashPosition(p, tableType);
LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); LZ4_putPositionOnHash(p, h, tableBase, tableType);
} }
/* LZ4_getIndexOnHash() : /* LZ4_getIndexOnHash() :
@@ -840,20 +862,18 @@ LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_
assert(0); return 0; /* forbidden case */ assert(0); return 0; /* forbidden case */
} }
static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase) static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType)
{ {
if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } assert(tableType == byPtr); (void)tableType;
if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; } { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }
{ const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
} }
LZ4_FORCE_INLINE const BYTE* LZ4_FORCE_INLINE const BYTE*
LZ4_getPosition(const BYTE* p, LZ4_getPosition(const BYTE* p,
const void* tableBase, tableType_t tableType, const void* tableBase, tableType_t tableType)
const BYTE* srcBase)
{ {
U32 const h = LZ4_hashPosition(p, tableType); U32 const h = LZ4_hashPosition(p, tableType);
return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); return LZ4_getPositionOnHash(h, tableBase, tableType);
} }
LZ4_FORCE_INLINE void LZ4_FORCE_INLINE void
@@ -897,9 +917,9 @@ LZ4_prepareTable(LZ4_stream_t_internal* const cctx,
cctx->dictSize = 0; cctx->dictSize = 0;
} }
/** LZ4_compress_generic() : /** LZ4_compress_generic_validated() :
* inlined, to ensure branches are decided at compilation time. * inlined, to ensure branches are decided at compilation time.
* Presumed already validated at this stage: * The following conditions are presumed already validated:
* - source != NULL * - source != NULL
* - inputSize > 0 * - inputSize > 0
*/ */
@@ -917,10 +937,10 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
const int acceleration) const int acceleration)
{ {
int result; int result;
const BYTE* ip = (const BYTE*) source; const BYTE* ip = (const BYTE*)source;
U32 const startIndex = cctx->currentOffset; U32 const startIndex = cctx->currentOffset;
const BYTE* base = (const BYTE*) source - startIndex; const BYTE* base = (const BYTE*)source - startIndex;
const BYTE* lowLimit; const BYTE* lowLimit;
const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;
@@ -928,7 +948,8 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;
const U32 dictSize = const U32 dictSize =
dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;
const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ const U32 dictDelta =
(dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with indexes in current context */
int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);
U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */
@@ -953,11 +974,11 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType); DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType);
assert(ip != NULL); assert(ip != NULL);
if (tableType == byU16) assert(inputSize<LZ4_64Klimit); /* Size too large (not within 64K limit) */
if (tableType == byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */
/* If init conditions are not met, we don't have to mark stream /* If init conditions are not met, we don't have to mark stream
* as having dirty context, since no action was taken yet */ * as having dirty context, since no action was taken yet */
if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */
if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */
if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */
assert(acceleration >= 1); assert(acceleration >= 1);
lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);
@@ -977,7 +998,12 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
/* First Byte */ /* First Byte */
LZ4_putPosition(ip, cctx->hashTable, tableType, base); { U32 const h = LZ4_hashPosition(ip, tableType);
if (tableType == byPtr) {
LZ4_putPositionOnHash(ip, h, cctx->hashTable, byPtr);
} else {
LZ4_putIndexOnHash(startIndex, h, cctx->hashTable, tableType);
} }
ip++; forwardH = LZ4_hashPosition(ip, tableType); ip++; forwardH = LZ4_hashPosition(ip, tableType);
/* Main Loop */ /* Main Loop */
@@ -1000,9 +1026,9 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
assert(ip < mflimitPlusOne); assert(ip < mflimitPlusOne);
match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType);
forwardH = LZ4_hashPosition(forwardIp, tableType); forwardH = LZ4_hashPosition(forwardIp, tableType);
LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType);
} while ( (match+LZ4_DISTANCE_MAX < ip) } while ( (match+LZ4_DISTANCE_MAX < ip)
|| (LZ4_read32(match) != LZ4_read32(ip)) ); || (LZ4_read32(match) != LZ4_read32(ip)) );
@@ -1073,7 +1099,10 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
/* Catch up */ /* Catch up */
filledIp = ip; filledIp = ip;
while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } assert(ip > anchor); /* this is always true as ip has been advanced before entering the main loop */
if ((match > lowLimit) && unlikely(ip[-1] == match[-1])) {
do { ip--; match--; } while (((ip > anchor) & (match > lowLimit)) && (unlikely(ip[-1] == match[-1])));
}
/* Encode Literals */ /* Encode Literals */
{ unsigned const litLength = (unsigned)(ip - anchor); { unsigned const litLength = (unsigned)(ip - anchor);
@@ -1088,7 +1117,7 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
goto _last_literals; goto _last_literals;
} }
if (litLength >= RUN_MASK) { if (litLength >= RUN_MASK) {
int len = (int)(litLength - RUN_MASK); unsigned len = litLength - RUN_MASK;
*token = (RUN_MASK<<ML_BITS); *token = (RUN_MASK<<ML_BITS);
for(; len >= 255 ; len-=255) *op++ = 255; for(; len >= 255 ; len-=255) *op++ = 255;
*op++ = (BYTE)len; *op++ = (BYTE)len;
@@ -1200,13 +1229,19 @@ _next_match:
if (ip >= mflimitPlusOne) break; if (ip >= mflimitPlusOne) break;
/* Fill table */ /* Fill table */
LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); { U32 const h = LZ4_hashPosition(ip-2, tableType);
if (tableType == byPtr) {
LZ4_putPositionOnHash(ip-2, h, cctx->hashTable, byPtr);
} else {
U32 const idx = (U32)((ip-2) - base);
LZ4_putIndexOnHash(idx, h, cctx->hashTable, tableType);
} }
/* Test next position */ /* Test next position */
if (tableType == byPtr) { if (tableType == byPtr) {
match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); match = LZ4_getPosition(ip, cctx->hashTable, tableType);
LZ4_putPosition(ip, cctx->hashTable, tableType, base); LZ4_putPosition(ip, cctx->hashTable, tableType);
if ( (match+LZ4_DISTANCE_MAX >= ip) if ( (match+LZ4_DISTANCE_MAX >= ip)
&& (LZ4_read32(match) == LZ4_read32(ip)) ) && (LZ4_read32(match) == LZ4_read32(ip)) )
{ token=op++; *token=0; goto _next_match; } { token=op++; *token=0; goto _next_match; }
@@ -1220,6 +1255,7 @@ _next_match:
if (dictDirective == usingDictCtx) { if (dictDirective == usingDictCtx) {
if (matchIndex < startIndex) { if (matchIndex < startIndex) {
/* there was no match, try the dictionary */ /* there was no match, try the dictionary */
assert(tableType == byU32);
matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
match = dictBase + matchIndex; match = dictBase + matchIndex;
lowLimit = dictionary; /* required for match length counter */ lowLimit = dictionary; /* required for match length counter */
@@ -1373,9 +1409,10 @@ int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int
*/ */
int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)
{ {
LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; LZ4_stream_t_internal* const ctx = &((LZ4_stream_t*)state)->internal_donotuse;
if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
assert(ctx != NULL);
if (dstCapacity >= LZ4_compressBound(srcSize)) { if (dstCapacity >= LZ4_compressBound(srcSize)) {
if (srcSize < LZ4_64Klimit) { if (srcSize < LZ4_64Klimit) {
@@ -1409,17 +1446,17 @@ int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst
} }
int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) int LZ4_compress_fast(const char* src, char* dest, int srcSize, int dstCapacity, int acceleration)
{ {
int result; int result;
#if (LZ4_HEAPMODE) #if (LZ4_HEAPMODE)
LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ LZ4_stream_t* const ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
if (ctxPtr == NULL) return 0; if (ctxPtr == NULL) return 0;
#else #else
LZ4_stream_t ctx; LZ4_stream_t ctx;
LZ4_stream_t* const ctxPtr = &ctx; LZ4_stream_t* const ctxPtr = &ctx;
#endif #endif
result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); result = LZ4_compress_fast_extState(ctxPtr, src, dest, srcSize, dstCapacity, acceleration);
#if (LZ4_HEAPMODE) #if (LZ4_HEAPMODE)
FREEMEM(ctxPtr); FREEMEM(ctxPtr);
@@ -1428,43 +1465,51 @@ int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutp
} }
int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize) int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity)
{ {
return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1); return LZ4_compress_fast(src, dst, srcSize, dstCapacity, 1);
} }
/* Note!: This function leaves the stream in an unclean/broken state! /* Note!: This function leaves the stream in an unclean/broken state!
* It is not safe to subsequently use the same state with a _fastReset() or * It is not safe to subsequently use the same state with a _fastReset() or
* _continue() call without resetting it. */ * _continue() call without resetting it. */
static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) static int LZ4_compress_destSize_extState_internal(LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration)
{ {
void* const s = LZ4_initStream(state, sizeof (*state)); void* const s = LZ4_initStream(state, sizeof (*state));
assert(s != NULL); (void)s; assert(s != NULL); (void)s;
if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */
return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, acceleration);
} else { } else {
if (*srcSizePtr < LZ4_64Klimit) { if (*srcSizePtr < LZ4_64Klimit) {
return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1); return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, acceleration);
} else { } else {
tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1); return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, acceleration);
} } } }
} }
int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration)
{
int const r = LZ4_compress_destSize_extState_internal((LZ4_stream_t*)state, src, dst, srcSizePtr, targetDstSize, acceleration);
/* clean the state on exit */
LZ4_initStream(state, sizeof (LZ4_stream_t));
return r;
}
int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
{ {
#if (LZ4_HEAPMODE) #if (LZ4_HEAPMODE)
LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ LZ4_stream_t* const ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
if (ctx == NULL) return 0; if (ctx == NULL) return 0;
#else #else
LZ4_stream_t ctxBody; LZ4_stream_t ctxBody;
LZ4_stream_t* ctx = &ctxBody; LZ4_stream_t* const ctx = &ctxBody;
#endif #endif
int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); int result = LZ4_compress_destSize_extState_internal(ctx, src, dst, srcSizePtr, targetDstSize, 1);
#if (LZ4_HEAPMODE) #if (LZ4_HEAPMODE)
FREEMEM(ctx); FREEMEM(ctx);
@@ -1533,14 +1578,17 @@ int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
#endif #endif
typedef enum { _ld_fast, _ld_slow } LoadDict_mode_e;
#define HASH_UNIT sizeof(reg_t) #define HASH_UNIT sizeof(reg_t)
int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) int LZ4_loadDict_internal(LZ4_stream_t* LZ4_dict,
const char* dictionary, int dictSize,
LoadDict_mode_e _ld)
{ {
LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
const tableType_t tableType = byU32; const tableType_t tableType = byU32;
const BYTE* p = (const BYTE*)dictionary; const BYTE* p = (const BYTE*)dictionary;
const BYTE* const dictEnd = p + dictSize; const BYTE* const dictEnd = p + dictSize;
const BYTE* base; U32 idx32;
DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);
@@ -1563,19 +1611,46 @@ int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
} }
if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
base = dictEnd - dict->currentOffset;
dict->dictionary = p; dict->dictionary = p;
dict->dictSize = (U32)(dictEnd - p); dict->dictSize = (U32)(dictEnd - p);
dict->tableType = (U32)tableType; dict->tableType = (U32)tableType;
idx32 = dict->currentOffset - dict->dictSize;
while (p <= dictEnd-HASH_UNIT) { while (p <= dictEnd-HASH_UNIT) {
LZ4_putPosition(p, dict->hashTable, tableType, base); U32 const h = LZ4_hashPosition(p, tableType);
p+=3; /* Note: overwriting => favors positions end of dictionary */
LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType);
p+=3; idx32+=3;
}
if (_ld == _ld_slow) {
/* Fill hash table with additional references, to improve compression capability */
p = dict->dictionary;
idx32 = dict->currentOffset - dict->dictSize;
while (p <= dictEnd-HASH_UNIT) {
U32 const h = LZ4_hashPosition(p, tableType);
U32 const limit = dict->currentOffset - 64 KB;
if (LZ4_getIndexOnHash(h, dict->hashTable, tableType) <= limit) {
/* Note: not overwriting => favors positions beginning of dictionary */
LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType);
}
p++; idx32++;
}
} }
return (int)dict->dictSize; return (int)dict->dictSize;
} }
int LZ4_loadDict(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
{
return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_fast);
}
int LZ4_loadDictSlow(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
{
return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_slow);
}
void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream)
{ {
const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL :
@@ -1707,7 +1782,7 @@ int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,
/* Hidden debug function, to force-test external dictionary mode */ /* Hidden debug function, to force-test external dictionary mode */
int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)
{ {
LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; LZ4_stream_t_internal* const streamPtr = &LZ4_dict->internal_donotuse;
int result; int result;
LZ4_renormDictT(streamPtr, srcSize); LZ4_renormDictT(streamPtr, srcSize);
@@ -1770,7 +1845,7 @@ typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;
* does not know end of input * does not know end of input
* presumes input is well formed * presumes input is well formed
* note : will consume at least one byte */ * note : will consume at least one byte */
size_t read_long_length_no_check(const BYTE** pp) static size_t read_long_length_no_check(const BYTE** pp)
{ {
size_t b, l = 0; size_t b, l = 0;
do { b = **pp; (*pp)++; l += b; } while (b==255); do { b = **pp; (*pp)++; l += b; } while (b==255);
@@ -1907,6 +1982,17 @@ read_variable_length(const BYTE** ip, const BYTE* ilimit,
if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */ if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */
return rvl_error; return rvl_error;
} }
s = **ip;
(*ip)++;
length += s;
if (unlikely((*ip) > ilimit)) { /* read limit reached */
return rvl_error;
}
/* accumulator overflow detection (32-bit mode only) */
if ((sizeof(length) < 8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {
return rvl_error;
}
if (likely(s != 255)) return length;
do { do {
s = **ip; s = **ip;
(*ip)++; (*ip)++;
@@ -1915,10 +2001,10 @@ read_variable_length(const BYTE** ip, const BYTE* ilimit,
return rvl_error; return rvl_error;
} }
/* accumulator overflow detection (32-bit mode only) */ /* accumulator overflow detection (32-bit mode only) */
if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { if ((sizeof(length) < 8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {
return rvl_error; return rvl_error;
} }
} while (s==255); } while (s == 255);
return length; return length;
} }
@@ -1984,63 +2070,73 @@ LZ4_decompress_generic(
* note : fast loop may show a regression for some client arm chips. */ * note : fast loop may show a regression for some client arm chips. */
#if LZ4_FAST_DEC_LOOP #if LZ4_FAST_DEC_LOOP
if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {
DEBUGLOG(6, "skip fast decode loop"); DEBUGLOG(6, "move to safe decode loop");
goto safe_decode; goto safe_decode;
} }
/* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */ /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */
DEBUGLOG(6, "using fast decode loop");
while (1) { while (1) {
/* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */
assert(oend - op >= FASTLOOP_SAFE_DISTANCE); assert(oend - op >= FASTLOOP_SAFE_DISTANCE);
assert(ip < iend); assert(ip < iend);
token = *ip++; token = *ip++;
length = token >> ML_BITS; /* literal length */ length = token >> ML_BITS; /* literal length */
DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length);
/* decode literal length */ /* decode literal length */
if (length == RUN_MASK) { if (length == RUN_MASK) {
size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);
if (addl == rvl_error) { goto _output_error; } if (addl == rvl_error) {
DEBUGLOG(6, "error reading long literal length");
goto _output_error;
}
length += addl; length += addl;
if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */
if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
/* copy literals */ /* copy literals */
cpy = op+length;
LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } if ((op+length>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }
LZ4_wildCopy32(op, ip, cpy); LZ4_wildCopy32(op, ip, op+length);
ip += length; op = cpy; ip += length; op += length;
} else { } else if (ip <= iend-(16 + 1/*max lit + offset + nextToken*/)) {
cpy = op+length;
DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length);
/* We don't need to check oend, since we check it once for each loop below */ /* We don't need to check oend, since we check it once for each loop below */
if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length);
/* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */ /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */
LZ4_memcpy(op, ip, 16); LZ4_memcpy(op, ip, 16);
ip += length; op = cpy; ip += length; op += length;
} else {
goto safe_literal_copy;
} }
/* get offset */ /* get offset */
offset = LZ4_readLE16(ip); ip+=2; offset = LZ4_readLE16(ip); ip+=2;
DEBUGLOG(6, "blockPos%6u: offset = %u", (unsigned)(op-(BYTE*)dst), (unsigned)offset);
match = op - offset; match = op - offset;
assert(match <= op); /* overflow check */ assert(match <= op); /* overflow check */
/* get matchlength */ /* get matchlength */
length = token & ML_MASK; length = token & ML_MASK;
DEBUGLOG(7, " match length token = %u (len==%u)", (unsigned)length, (unsigned)length+MINMATCH);
if (length == ML_MASK) { if (length == ML_MASK) {
size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);
if (addl == rvl_error) { goto _output_error; } if (addl == rvl_error) {
DEBUGLOG(5, "error reading long match length");
goto _output_error;
}
length += addl; length += addl;
length += MINMATCH; length += MINMATCH;
DEBUGLOG(7, " long match length == %u", (unsigned)length);
if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */
if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */
if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
goto safe_match_copy; goto safe_match_copy;
} }
} else { } else {
length += MINMATCH; length += MINMATCH;
if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
DEBUGLOG(7, "moving to safe_match_copy (ml==%u)", (unsigned)length);
goto safe_match_copy; goto safe_match_copy;
} }
@@ -2058,7 +2154,10 @@ LZ4_decompress_generic(
continue; continue;
} } } } } }
if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ if ( checkOffset && (unlikely(match + dictSize < lowPrefix)) ) {
DEBUGLOG(5, "Error : pos=%zi, offset=%zi => outside buffers", op-lowPrefix, op-match);
goto _output_error;
}
/* match starting within external dictionary */ /* match starting within external dictionary */
if ((dict==usingExtDict) && (match < lowPrefix)) { if ((dict==usingExtDict) && (match < lowPrefix)) {
assert(dictEnd != NULL); assert(dictEnd != NULL);
@@ -2067,7 +2166,8 @@ LZ4_decompress_generic(
DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd");
length = MIN(length, (size_t)(oend-op)); length = MIN(length, (size_t)(oend-op));
} else { } else {
goto _output_error; /* end-of-block condition violated */ DEBUGLOG(6, "end-of-block condition violated")
goto _output_error;
} } } }
if (length <= (size_t)(lowPrefix-match)) { if (length <= (size_t)(lowPrefix-match)) {
@@ -2107,10 +2207,12 @@ LZ4_decompress_generic(
#endif #endif
/* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */
DEBUGLOG(6, "using safe decode loop");
while (1) { while (1) {
assert(ip < iend); assert(ip < iend);
token = *ip++; token = *ip++;
length = token >> ML_BITS; /* literal length */ length = token >> ML_BITS; /* literal length */
DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length);
/* A two-stage shortcut for the most common case: /* A two-stage shortcut for the most common case:
* 1) If the literal length is 0..14, and there is enough space, * 1) If the literal length is 0..14, and there is enough space,
@@ -2131,6 +2233,7 @@ LZ4_decompress_generic(
/* The second stage: prepare for match copying, decode full info. /* The second stage: prepare for match copying, decode full info.
* If it doesn't work out, the info won't be wasted. */ * If it doesn't work out, the info won't be wasted. */
length = token & ML_MASK; /* match length */ length = token & ML_MASK; /* match length */
DEBUGLOG(7, "blockPos%6u: matchLength token = %u (len=%u)", (unsigned)(op-(BYTE*)dst), (unsigned)length, (unsigned)length + 4);
offset = LZ4_readLE16(ip); ip += 2; offset = LZ4_readLE16(ip); ip += 2;
match = op - offset; match = op - offset;
assert(match <= op); /* check overflow */ assert(match <= op); /* check overflow */
@@ -2162,11 +2265,12 @@ LZ4_decompress_generic(
if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
} }
/* copy literals */
cpy = op+length;
#if LZ4_FAST_DEC_LOOP #if LZ4_FAST_DEC_LOOP
safe_literal_copy: safe_literal_copy:
#endif #endif
/* copy literals */
cpy = op+length;
LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) { if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) {
/* We've either hit the input parsing restriction or the output parsing restriction. /* We've either hit the input parsing restriction or the output parsing restriction.
@@ -2202,9 +2306,10 @@ LZ4_decompress_generic(
* so check that we exactly consume the input and don't overrun the output buffer. * so check that we exactly consume the input and don't overrun the output buffer.
*/ */
if ((ip+length != iend) || (cpy > oend)) { if ((ip+length != iend) || (cpy > oend)) {
DEBUGLOG(6, "should have been last run of literals") DEBUGLOG(5, "should have been last run of literals")
DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); DEBUGLOG(5, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend);
DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); DEBUGLOG(5, "or cpy(%p) > (oend-MFLIMIT)(%p)", cpy, oend-MFLIMIT);
DEBUGLOG(5, "after writing %u bytes / %i bytes available", (unsigned)(op-(BYTE*)dst), outputSize);
goto _output_error; goto _output_error;
} }
} }
@@ -2230,6 +2335,7 @@ LZ4_decompress_generic(
/* get matchlength */ /* get matchlength */
length = token & ML_MASK; length = token & ML_MASK;
DEBUGLOG(7, "blockPos%6u: matchLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length);
_copy_match: _copy_match:
if (length == ML_MASK) { if (length == ML_MASK) {
@@ -2319,7 +2425,7 @@ LZ4_decompress_generic(
while (op < cpy) { *op++ = *match++; } while (op < cpy) { *op++ = *match++; }
} else { } else {
LZ4_memcpy(op, match, 8); LZ4_memcpy(op, match, 8);
if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); }
} }
op = cpy; /* wildcopy correction */ op = cpy; /* wildcopy correction */
} }
@@ -2414,6 +2520,7 @@ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
int compressedSize, int maxOutputSize, int compressedSize, int maxOutputSize,
const void* dictStart, size_t dictSize) const void* dictStart, size_t dictSize)
{ {
DEBUGLOG(5, "LZ4_decompress_safe_forceExtDict");
return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
decode_full_block, usingExtDict, decode_full_block, usingExtDict,
(BYTE*)dest, (const BYTE*)dictStart, dictSize); (BYTE*)dest, (const BYTE*)dictStart, dictSize);

View File

@@ -1,7 +1,7 @@
/* /*
* LZ4 - Fast LZ compression algorithm * LZ4 - Fast LZ compression algorithm
* Header File * Header File
* Copyright (C) 2011-2020, Yann Collet. * Copyright (C) 2011-2023, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
@@ -127,8 +127,8 @@
/*------ Version ------*/ /*------ Version ------*/
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ #define LZ4_VERSION_MINOR 10 /* for new (non-breaking) interface capabilities */
#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */ #define LZ4_VERSION_RELEASE 0 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
@@ -145,23 +145,25 @@ LZ4LIB_API const char* LZ4_versionString (void); /**< library version string;
/*-************************************ /*-************************************
* Tuning parameter * Tuning memory usage
**************************************/ **************************************/
#define LZ4_MEMORY_USAGE_MIN 10
#define LZ4_MEMORY_USAGE_DEFAULT 14
#define LZ4_MEMORY_USAGE_MAX 20
/*! /*!
* LZ4_MEMORY_USAGE : * LZ4_MEMORY_USAGE :
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; ) * Can be selected at compile time, by setting LZ4_MEMORY_USAGE.
* Increasing memory usage improves compression ratio, at the cost of speed. * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB)
* Increasing memory usage improves compression ratio, generally at the cost of speed.
* Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache * Default value is 14, for 16KB, which nicely fits into most L1 caches.
*/ */
#ifndef LZ4_MEMORY_USAGE #ifndef LZ4_MEMORY_USAGE
# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT # define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT
#endif #endif
/* These are absolute limits, they should not be changed by users */
#define LZ4_MEMORY_USAGE_MIN 10
#define LZ4_MEMORY_USAGE_DEFAULT 14
#define LZ4_MEMORY_USAGE_MAX 20
#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN) #if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)
# error "LZ4_MEMORY_USAGE is too small !" # error "LZ4_MEMORY_USAGE is too small !"
#endif #endif
@@ -190,8 +192,9 @@ LZ4LIB_API const char* LZ4_versionString (void); /**< library version string;
LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
/*! LZ4_decompress_safe() : /*! LZ4_decompress_safe() :
* compressedSize : is the exact complete size of the compressed block. * @compressedSize : is the exact complete size of the compressed block.
* dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. * @dstCapacity : is the size of destination buffer (which must be already allocated),
* presumed an upper bound of decompressed size.
* @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
* If destination buffer is not large enough, decoding will stop and output an error code (negative value). * If destination buffer is not large enough, decoding will stop and output an error code (negative value).
* If the source stream is detected malformed, the function will stop decoding and return a negative result. * If the source stream is detected malformed, the function will stop decoding and return a negative result.
@@ -243,20 +246,20 @@ LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int d
LZ4LIB_API int LZ4_sizeofState(void); LZ4LIB_API int LZ4_sizeofState(void);
LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_compress_destSize() : /*! LZ4_compress_destSize() :
* Reverse the logic : compresses as much data as possible from 'src' buffer * Reverse the logic : compresses as much data as possible from 'src' buffer
* into already allocated buffer 'dst', of size >= 'targetDestSize'. * into already allocated buffer 'dst', of size >= 'dstCapacity'.
* This function either compresses the entire 'src' content into 'dst' if it's large enough, * This function either compresses the entire 'src' content into 'dst' if it's large enough,
* or fill 'dst' buffer completely with as much data as possible from 'src'. * or fill 'dst' buffer completely with as much data as possible from 'src'.
* note: acceleration parameter is fixed to "default". * note: acceleration parameter is fixed to "default".
* *
* *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. * *srcSizePtr : in+out parameter. Initially contains size of input.
* Will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
* New value is necessarily <= input value. * New value is necessarily <= input value.
* @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) * @return : Nb bytes written into 'dst' (necessarily <= dstCapacity)
* or 0 if compression fails. * or 0 if compression fails.
* *
* Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+): * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed in v1.9.2+):
* the produced compressed content could, in specific circumstances, * the produced compressed content could, in specific circumstances,
* require to be decompressed into a destination buffer larger * require to be decompressed into a destination buffer larger
* by at least 1 byte than the content to decompress. * by at least 1 byte than the content to decompress.
@@ -267,8 +270,7 @@ LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* d
* a dstCapacity which is > decompressedSize, by at least 1 byte. * a dstCapacity which is > decompressedSize, by at least 1 byte.
* See https://github.com/lz4/lz4/issues/859 for details * See https://github.com/lz4/lz4/issues/859 for details
*/ */
LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); LZ4LIB_API int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize);
/*! LZ4_decompress_safe_partial() : /*! LZ4_decompress_safe_partial() :
* Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
@@ -312,7 +314,7 @@ LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcS
***********************************************/ ***********************************************/
typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
/** /*!
Note about RC_INVOKED Note about RC_INVOKED
- RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio). - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).
@@ -362,13 +364,58 @@ LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
* LZ4_loadDict() triggers a reset, so any previous data will be forgotten. * LZ4_loadDict() triggers a reset, so any previous data will be forgotten.
* The same dictionary will have to be loaded on decompression side for successful decoding. * The same dictionary will have to be loaded on decompression side for successful decoding.
* Dictionary are useful for better compression of small data (KB range). * Dictionary are useful for better compression of small data (KB range).
* While LZ4 accept any input as dictionary, * While LZ4 itself accepts any input as dictionary, dictionary efficiency is also a topic.
* results are generally better when using Zstandard's Dictionary Builder. * When in doubt, employ the Zstandard's Dictionary Builder.
* Loading a size of 0 is allowed, and is the same as reset. * Loading a size of 0 is allowed, and is the same as reset.
* @return : loaded dictionary size, in bytes (necessarily <= 64 KB) * @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded)
*/ */
LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
/*! LZ4_loadDictSlow() : v1.10.0+
* Same as LZ4_loadDict(),
* but uses a bit more cpu to reference the dictionary content more thoroughly.
* This is expected to slightly improve compression ratio.
* The extra-cpu cost is likely worth it if the dictionary is re-used across multiple sessions.
* @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded)
*/
LZ4LIB_API int LZ4_loadDictSlow(LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
/*! LZ4_attach_dictionary() : stable since v1.10.0
*
* This allows efficient re-use of a static dictionary multiple times.
*
* Rather than re-loading the dictionary buffer into a working context before
* each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
* working LZ4_stream_t, this function introduces a no-copy setup mechanism,
* in which the working stream references @dictionaryStream in-place.
*
* Several assumptions are made about the state of @dictionaryStream.
* Currently, only states which have been prepared by LZ4_loadDict() or
* LZ4_loadDictSlow() should be expected to work.
*
* Alternatively, the provided @dictionaryStream may be NULL,
* in which case any existing dictionary stream is unset.
*
* If a dictionary is provided, it replaces any pre-existing stream history.
* The dictionary contents are the only history that can be referenced and
* logically immediately precede the data compressed in the first subsequent
* compression call.
*
* The dictionary will only remain attached to the working stream through the
* first compression call, at the end of which it is cleared.
* @dictionaryStream stream (and source buffer) must remain in-place / accessible / unchanged
* through the completion of the compression session.
*
* Note: there is no equivalent LZ4_attach_*() method on the decompression side
* because there is no initialization cost, hence no need to share the cost across multiple sessions.
* To decompress LZ4 blocks using dictionary, attached or not,
* just employ the regular LZ4_setStreamDecode() for streaming,
* or the stateless LZ4_decompress_safe_usingDict() for one-shot decompression.
*/
LZ4LIB_API void
LZ4_attach_dictionary(LZ4_stream_t* workingStream,
const LZ4_stream_t* dictionaryStream);
/*! LZ4_compress_fast_continue() : /*! LZ4_compress_fast_continue() :
* Compress 'src' content using data from previously compressed blocks, for better compression ratio. * Compress 'src' content using data from previously compressed blocks, for better compression ratio.
* 'dst' buffer must be already allocated. * 'dst' buffer must be already allocated.
@@ -444,11 +491,24 @@ LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const
LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ #define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */
/*! LZ4_decompress_*_continue() : /*! LZ4_decompress_safe_continue() :
* These decoding functions allow decompression of consecutive blocks in "streaming" mode. * This decoding function allows decompression of consecutive blocks in "streaming" mode.
* A block is an unsplittable entity, it must be presented entirely to a decompression function. * The difference with the usual independent blocks is that
* Decompression functions only accepts one block at a time. * new blocks are allowed to find references into former blocks.
* The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. * A block is an unsplittable entity, and must be presented entirely to the decompression function.
* LZ4_decompress_safe_continue() only accepts one block at a time.
* It's modeled after `LZ4_decompress_safe()` and behaves similarly.
*
* @LZ4_streamDecode : decompression state, tracking the position in memory of past data
* @compressedSize : exact complete size of one compressed block.
* @dstCapacity : size of destination buffer (which must be already allocated),
* must be an upper bound of decompressed size.
* @return : number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
* If destination buffer is not large enough, decoding will stop and output an error code (negative value).
* If the source stream is detected malformed, the function will stop decoding and return a negative result.
*
* The last 64KB of previously decoded data *must* remain available and unmodified
* at the memory position where they were previously decoded.
* If less than 64KB of data has been decoded, all the data must be present. * If less than 64KB of data has been decoded, all the data must be present.
* *
* Special : if decompression side sets a ring buffer, it must respect one of the following conditions : * Special : if decompression side sets a ring buffer, it must respect one of the following conditions :
@@ -475,10 +535,10 @@ LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,
int srcSize, int dstCapacity); int srcSize, int dstCapacity);
/*! LZ4_decompress_*_usingDict() : /*! LZ4_decompress_safe_usingDict() :
* These decoding functions work the same as * Works the same as
* a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_safe_continue()
* They are stand-alone, and don't need an LZ4_streamDecode_t structure. * However, it's stateless: it doesn't need any LZ4_streamDecode_t state.
* Dictionary is presumed stable : it must remain accessible and unmodified during decompression. * Dictionary is presumed stable : it must remain accessible and unmodified during decompression.
* Performance tip : Decompression speed can be substantially increased * Performance tip : Decompression speed can be substantially increased
* when dst == dictStart + dictSize. * when dst == dictStart + dictSize.
@@ -488,6 +548,12 @@ LZ4_decompress_safe_usingDict(const char* src, char* dst,
int srcSize, int dstCapacity, int srcSize, int dstCapacity,
const char* dictStart, int dictSize); const char* dictStart, int dictSize);
/*! LZ4_decompress_safe_partial_usingDict() :
* Behaves the same as LZ4_decompress_safe_partial()
* with the added ability to specify a memory segment for past data.
* Performance tip : Decompression speed can be substantially increased
* when dst == dictStart + dictSize.
*/
LZ4LIB_API int LZ4LIB_API int
LZ4_decompress_safe_partial_usingDict(const char* src, char* dst, LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,
int compressedSize, int compressedSize,
@@ -529,9 +595,9 @@ LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,
#define TRACY_LZ4_STATIC_3504398509 #define TRACY_LZ4_STATIC_3504398509
#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS #ifdef LZ4_PUBLISH_STATIC_FUNCTIONS
#define LZ4LIB_STATIC_API LZ4LIB_API # define LZ4LIB_STATIC_API LZ4LIB_API
#else #else
#define LZ4LIB_STATIC_API # define LZ4LIB_STATIC_API
#endif #endif
namespace tracy namespace tracy
@@ -549,36 +615,11 @@ namespace tracy
*/ */
LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_attach_dictionary() : /*! LZ4_compress_destSize_extState() : introduced in v1.10.0
* This is an experimental API that allows * Same as LZ4_compress_destSize(), but using an externally allocated state.
* efficient use of a static dictionary many times. * Also: exposes @acceleration
*
* Rather than re-loading the dictionary buffer into a working context before
* each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
* working LZ4_stream_t, this function introduces a no-copy setup mechanism,
* in which the working stream references the dictionary stream in-place.
*
* Several assumptions are made about the state of the dictionary stream.
* Currently, only streams which have been prepared by LZ4_loadDict() should
* be expected to work.
*
* Alternatively, the provided dictionaryStream may be NULL,
* in which case any existing dictionary stream is unset.
*
* If a dictionary is provided, it replaces any pre-existing stream history.
* The dictionary contents are the only history that can be referenced and
* logically immediately precede the data compressed in the first subsequent
* compression call.
*
* The dictionary will only remain attached to the working stream through the
* first compression call, at the end of which it is cleared. The dictionary
* stream (and source buffer) must remain in-place / accessible / unchanged
* through the completion of the first compression call on the stream.
*/ */
LZ4LIB_STATIC_API void int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration);
LZ4_attach_dictionary(LZ4_stream_t* workingStream,
const LZ4_stream_t* dictionaryStream);
/*! In-place compression and decompression /*! In-place compression and decompression
* *
@@ -694,7 +735,7 @@ struct LZ4_stream_t_internal {
/* Implicit padding to ensure structure is aligned */ /* Implicit padding to ensure structure is aligned */
}; };
#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */ #define LZ4_STREAM_MINSIZE ((1UL << (LZ4_MEMORY_USAGE)) + 32) /* static size, for inter-version compatibility */
union LZ4_stream_u { union LZ4_stream_u {
char minStateSize[LZ4_STREAM_MINSIZE]; char minStateSize[LZ4_STREAM_MINSIZE];
LZ4_stream_t_internal internal_donotuse; LZ4_stream_t_internal internal_donotuse;
@@ -715,7 +756,7 @@ union LZ4_stream_u {
* Note2: An LZ4_stream_t structure guarantees correct alignment and size. * Note2: An LZ4_stream_t structure guarantees correct alignment and size.
* Note3: Before v1.9.0, use LZ4_resetStream() instead * Note3: Before v1.9.0, use LZ4_resetStream() instead
**/ **/
LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* stateBuffer, size_t size);
/*! LZ4_streamDecode_t : /*! LZ4_streamDecode_t :
@@ -827,11 +868,12 @@ LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4
* But they may happen if input data is invalid (error or intentional tampering). * But they may happen if input data is invalid (error or intentional tampering).
* As a consequence, use these functions in trusted environments with trusted data **only**. * As a consequence, use these functions in trusted environments with trusted data **only**.
*/ */
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial() instead")
LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") LZ4_DEPRECATED("This function is deprecated and unsafe. Consider migrating towards LZ4_decompress_safe_continue() instead. "
"Note that the contract will change (requires block's compressed size, instead of decompressed size)")
LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial_usingDict() instead")
LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
/*! LZ4_resetStream() : /*! LZ4_resetStream() :

File diff suppressed because it is too large Load Diff

View File

@@ -40,7 +40,7 @@
/* --- Useful constants --- */ /* --- Useful constants --- */
#define LZ4HC_CLEVEL_MIN 3 #define LZ4HC_CLEVEL_MIN 2
#define LZ4HC_CLEVEL_DEFAULT 9 #define LZ4HC_CLEVEL_DEFAULT 9
#define LZ4HC_CLEVEL_OPT_MIN 10 #define LZ4HC_CLEVEL_OPT_MIN 10
#define LZ4HC_CLEVEL_MAX 12 #define LZ4HC_CLEVEL_MAX 12
@@ -124,6 +124,8 @@ LZ4LIB_API int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr);
After reset, a first "fictional block" can be designated as initial dictionary, After reset, a first "fictional block" can be designated as initial dictionary,
using LZ4_loadDictHC() (Optional). using LZ4_loadDictHC() (Optional).
Note: In order for LZ4_loadDictHC() to create the correct data structure,
it is essential to set the compression level _before_ loading the dictionary.
Invoke LZ4_compress_HC_continue() to compress each successive block. Invoke LZ4_compress_HC_continue() to compress each successive block.
The number of blocks is unlimited. The number of blocks is unlimited.
@@ -133,12 +135,12 @@ LZ4LIB_API int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr);
It's allowed to update compression level anytime between blocks, It's allowed to update compression level anytime between blocks,
using LZ4_setCompressionLevel() (experimental). using LZ4_setCompressionLevel() (experimental).
'dst' buffer should be sized to handle worst case scenarios @dst buffer should be sized to handle worst case scenarios
(see LZ4_compressBound(), it ensures compression success). (see LZ4_compressBound(), it ensures compression success).
In case of failure, the API does not guarantee recovery, In case of failure, the API does not guarantee recovery,
so the state _must_ be reset. so the state _must_ be reset.
To ensure compression success To ensure compression success
whenever `dst` buffer size cannot be made >= LZ4_compressBound(), whenever @dst buffer size cannot be made >= LZ4_compressBound(),
consider using LZ4_compress_HC_continue_destSize(). consider using LZ4_compress_HC_continue_destSize().
Whenever previous input blocks can't be preserved unmodified in-place during compression of next blocks, Whenever previous input blocks can't be preserved unmodified in-place during compression of next blocks,
@@ -174,6 +176,34 @@ LZ4LIB_API int LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr
LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize); LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize);
/*! LZ4_attach_HC_dictionary() : stable since v1.10.0
* This API allows for the efficient re-use of a static dictionary many times.
*
* Rather than re-loading the dictionary buffer into a working context before
* each compression, or copying a pre-loaded dictionary's LZ4_streamHC_t into a
* working LZ4_streamHC_t, this function introduces a no-copy setup mechanism,
* in which the working stream references the dictionary stream in-place.
*
* Several assumptions are made about the state of the dictionary stream.
* Currently, only streams which have been prepared by LZ4_loadDictHC() should
* be expected to work.
*
* Alternatively, the provided dictionary stream pointer may be NULL, in which
* case any existing dictionary stream is unset.
*
* A dictionary should only be attached to a stream without any history (i.e.,
* a stream that has just been reset).
*
* The dictionary will remain attached to the working stream only for the
* current stream session. Calls to LZ4_resetStreamHC(_fast) will remove the
* dictionary context association from the working stream. The dictionary
* stream (and source buffer) must remain in-place / accessible / unchanged
* through the lifetime of the stream session.
*/
LZ4LIB_API void
LZ4_attach_HC_dictionary(LZ4_streamHC_t* working_stream,
const LZ4_streamHC_t* dictionary_stream);
/*^********************************************** /*^**********************************************
* !!!!!! STATIC LINKING ONLY !!!!!! * !!!!!! STATIC LINKING ONLY !!!!!!
@@ -202,18 +232,18 @@ LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, in
typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal; typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal;
struct LZ4HC_CCtx_internal struct LZ4HC_CCtx_internal
{ {
LZ4_u32 hashTable[LZ4HC_HASHTABLESIZE]; LZ4_u32 hashTable[LZ4HC_HASHTABLESIZE];
LZ4_u16 chainTable[LZ4HC_MAXD]; LZ4_u16 chainTable[LZ4HC_MAXD];
const LZ4_byte* end; /* next block here to continue on current prefix */ const LZ4_byte* end; /* next block here to continue on current prefix */
const LZ4_byte* prefixStart; /* Indexes relative to this position */ const LZ4_byte* prefixStart; /* Indexes relative to this position */
const LZ4_byte* dictStart; /* alternate reference for extDict */ const LZ4_byte* dictStart; /* alternate reference for extDict */
LZ4_u32 dictLimit; /* below that point, need extDict */ LZ4_u32 dictLimit; /* below that point, need extDict */
LZ4_u32 lowLimit; /* below that point, no more dict */ LZ4_u32 lowLimit; /* below that point, no more history */
LZ4_u32 nextToUpdate; /* index from which to continue dictionary update */ LZ4_u32 nextToUpdate; /* index from which to continue dictionary update */
short compressionLevel; short compressionLevel;
LZ4_i8 favorDecSpeed; /* favor decompression speed if this flag set, LZ4_i8 favorDecSpeed; /* favor decompression speed if this flag set,
otherwise, favor compression ratio */ otherwise, favor compression ratio */
LZ4_i8 dirty; /* stream has to be fully reset if this flag is set */ LZ4_i8 dirty; /* stream has to be fully reset if this flag is set */
const LZ4HC_CCtx_internal* dictCtx; const LZ4HC_CCtx_internal* dictCtx;
}; };
@@ -370,35 +400,6 @@ LZ4LIB_STATIC_API int LZ4_compress_HC_extStateHC_fastReset (
int srcSize, int dstCapacity, int srcSize, int dstCapacity,
int compressionLevel); int compressionLevel);
/*! LZ4_attach_HC_dictionary() :
* This is an experimental API that allows for the efficient use of a
* static dictionary many times.
*
* Rather than re-loading the dictionary buffer into a working context before
* each compression, or copying a pre-loaded dictionary's LZ4_streamHC_t into a
* working LZ4_streamHC_t, this function introduces a no-copy setup mechanism,
* in which the working stream references the dictionary stream in-place.
*
* Several assumptions are made about the state of the dictionary stream.
* Currently, only streams which have been prepared by LZ4_loadDictHC() should
* be expected to work.
*
* Alternatively, the provided dictionary stream pointer may be NULL, in which
* case any existing dictionary stream is unset.
*
* A dictionary should only be attached to a stream without any history (i.e.,
* a stream that has just been reset).
*
* The dictionary will remain attached to the working stream only for the
* current stream session. Calls to LZ4_resetStreamHC(_fast) will remove the
* dictionary context association from the working stream. The dictionary
* stream (and source buffer) must remain in-place / accessible / unchanged
* through the lifetime of the stream session.
*/
LZ4LIB_STATIC_API void LZ4_attach_HC_dictionary(
LZ4_streamHC_t *working_stream,
const LZ4_streamHC_t *dictionary_stream);
} }
#endif /* LZ4_HC_SLO_098092834 */ #endif /* LZ4_HC_SLO_098092834 */

View File

@@ -43,13 +43,13 @@ using TracyWebGPUCtx = void*;
#include "Tracy.hpp" #include "Tracy.hpp"
#include "../client/TracyProfiler.hpp" #include "../client/TracyProfiler.hpp"
#include "../client/TracyCallstack.hpp" #include "../client/TracyCallstack.hpp"
#include "../client/TracyFastVector.hpp"
#include "../common/TracyAlign.hpp" #include "../common/TracyAlign.hpp"
#include "../common/TracyAlloc.hpp" #include "../common/TracyAlloc.hpp"
#include "../common/TracyAssert.hpp" #include "../common/TracyAssert.hpp"
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
#include <vector>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@@ -91,6 +91,115 @@ extern "C" int32_t IsDebuggerPresent(void);
namespace tracy namespace tracy
{ {
struct TracyEmitter
{
static tracy_force_inline void SubmitQueueItem(tracy::QueueItem* item)
{
#ifdef TRACY_ON_DEMAND
GetProfiler().DeferItem(*item);
#endif
Profiler::QueueSerialFinish();
}
static int32_t EmitGpuNewContext(uint64_t cpuTimestamp, uint64_t gpuTimestamp, double period)
{
ZoneScoped;
int32_t ctxId = NextGpuContextId();
ZoneValue(ctxId);
auto* item = Profiler::QueueSerial();
MemWrite(&item->hdr.type, QueueType::GpuNewContext);
MemWrite(&item->gpuNewContext.cpuTime, static_cast<int64_t>(cpuTimestamp));
MemWrite(&item->gpuNewContext.gpuTime, static_cast<int64_t>(gpuTimestamp));
MemWrite(&item->gpuNewContext.thread, static_cast<uint32_t>(0));
MemWrite(&item->gpuNewContext.period, static_cast<float>(period));
MemWrite(&item->gpuNewContext.context, static_cast<uint8_t>(ctxId));
MemWrite(&item->gpuNewContext.flags, GpuContextFlags(0)); // no calibration available
MemWrite(&item->gpuNewContext.type, GpuContextType::WebGPU);
SubmitQueueItem(item);
return ctxId;
}
static void EmitGpuContextName(uint32_t ctxId, const char* name, uint16_t len)
{
auto ptr = (char*)tracy_malloc(len);
memcpy(ptr, name, len);
auto item = Profiler::QueueSerial();
MemWrite(&item->hdr.type, QueueType::GpuContextName);
MemWrite(&item->gpuContextNameFat.context, static_cast<uint8_t>(ctxId));
MemWrite(&item->gpuContextNameFat.ptr, (uint64_t)ptr);
MemWrite(&item->gpuContextNameFat.size, len);
SubmitQueueItem(item);
}
static tracy_force_inline void EmitGpuTime(uint32_t ctxId, uint64_t gpuTimestamp, uint32_t queryId)
{
auto* item = Profiler::QueueSerial();
MemWrite(&item->hdr.type, QueueType::GpuTime);
MemWrite(&item->gpuTime.gpuTime, static_cast<int64_t>(gpuTimestamp));
MemWrite(&item->gpuTime.queryId, static_cast<uint16_t>(queryId));
MemWrite(&item->gpuTime.context, static_cast<uint8_t>(ctxId));
Profiler::QueueSerialFinish();
}
static tracy_force_inline void EmitGpuZoneBegin(uint32_t ctxId, uint32_t queryId, const SourceLocationData* srcLocation, int32_t callstackDepth, uint32_t sourceLine, const char* sourceFile, size_t sourceFileLen, const char* functionName, size_t functionNameLen, const char* zoneName, size_t zoneNameLen)
{
const bool captureCallstack = callstackDepth > 0 && has_callstack();
const bool transientZone = srcLocation == nullptr;
uint64_t srcLocationAddr = reinterpret_cast<uint64_t>(srcLocation);
QueueItem* item = nullptr;
QueueType itemType;
if (transientZone)
{
srcLocationAddr = Profiler::AllocSourceLocation(sourceLine, sourceFile, sourceFileLen, functionName, functionNameLen, zoneName, zoneNameLen);
if (captureCallstack)
{
item = Profiler::QueueSerialCallstack(Callstack(callstackDepth));
itemType = QueueType::GpuZoneBeginAllocSrcLocCallstackSerial;
}
else
{
item = Profiler::QueueSerial();
itemType = QueueType::GpuZoneBeginAllocSrcLocSerial;
}
}
else
{
if (captureCallstack)
{
item = Profiler::QueueSerialCallstack(Callstack(callstackDepth));
itemType = QueueType::GpuZoneBeginCallstackSerial;
}
else
{
item = Profiler::QueueSerial();
itemType = QueueType::GpuZoneBeginSerial;
}
}
MemWrite(&item->hdr.type, itemType);
MemWrite(&item->gpuZoneBegin.cpuTime, Profiler::GetTime());
MemWrite(&item->gpuZoneBegin.srcloc, srcLocationAddr);
MemWrite(&item->gpuZoneBegin.thread, GetThreadHandle());
MemWrite(&item->gpuZoneBegin.queryId, static_cast<uint16_t>(queryId));
MemWrite(&item->gpuZoneBegin.context, static_cast<uint8_t>(ctxId));
Profiler::QueueSerialFinish();
}
static tracy_force_inline void EmitGpuZoneEnd(uint32_t ctxId, uint32_t queryId)
{
auto* item = Profiler::QueueSerial();
MemWrite(&item->hdr.type, QueueType::GpuZoneEndSerial);
MemWrite(&item->gpuZoneEnd.cpuTime, Profiler::GetTime());
MemWrite(&item->gpuZoneEnd.thread, GetThreadHandle());
MemWrite(&item->gpuZoneEnd.queryId, static_cast<uint16_t>(queryId));
MemWrite(&item->gpuZoneEnd.context, static_cast<uint8_t>(ctxId));
Profiler::QueueSerialFinish();
}
};
class WebGPUQueueCtx class WebGPUQueueCtx
{ {
@@ -113,7 +222,9 @@ namespace tracy
}; };
static_assert(std::atomic<WGPUMapAsyncStatus>::is_always_lock_free, "WGPUMapAsyncStatus must be lock-free atomic"); static_assert(std::atomic<WGPUMapAsyncStatus>::is_always_lock_free, "WGPUMapAsyncStatus must be lock-free atomic");
WGPUQuerySet m_querySet = nullptr; static constexpr uint32_t QueryLimit = 64 * 1024; // max 64K queries in-flight
uint32_t m_queriesPerSet = 0; // per-set size (power of two), negotiated at init
FastVector<WGPUQuerySet> m_querySets { 16 };
WGPUBuffer m_resolveBuffer = nullptr; WGPUBuffer m_resolveBuffer = nullptr;
ReadbackStage m_readbackReel [3]; ReadbackStage m_readbackReel [3];
std::atomic<int> m_writeIdx {0}; std::atomic<int> m_writeIdx {0};
@@ -122,9 +233,7 @@ namespace tracy
atomic_counter m_queryCounter = 0; atomic_counter m_queryCounter = 0;
atomic_counter m_previousCheckpoint = 0; atomic_counter m_previousCheckpoint = 0;
uint32_t m_queryLimit = 0; FastVector<uint64_t> m_shadowBuffer {QueryLimit};
std::vector<uint64_t> m_shadowBuffer;
using WallTime = std::chrono::steady_clock::time_point; using WallTime = std::chrono::steady_clock::time_point;
static tracy_force_inline auto GetWallTime() { return WallTime::clock::now(); } static tracy_force_inline auto GetWallTime() { return WallTime::clock::now(); }
@@ -232,14 +341,6 @@ namespace tracy
} }
} m_calibration; } m_calibration;
tracy_force_inline void SubmitQueueItem(tracy::QueueItem* item)
{
#ifdef TRACY_ON_DEMAND
GetProfiler().DeferItem(*item);
#endif
Profiler::QueueSerialFinish();
}
bool CalibrateClocks(uint64_t& outCpuTime, uint64_t& outGpuTime, double& period) bool CalibrateClocks(uint64_t& outCpuTime, uint64_t& outGpuTime, double& period)
{ {
// WebGPU does not have any clock calibration API. // WebGPU does not have any clock calibration API.
@@ -296,9 +397,10 @@ namespace tracy
WGPURenderPipeline calibPipeline = wgpuDeviceCreateRenderPipeline(m_device, &pipeDesc); WGPURenderPipeline calibPipeline = wgpuDeviceCreateRenderPipeline(m_device, &pipeDesc);
if (!calibPipeline) { wgpuTextureViewRelease(texView); wgpuTextureRelease(tex); wgpuShaderModuleRelease(calibShader); TracyWebGPUPanic("Failed to create calibration pipeline.", return false); } if (!calibPipeline) { wgpuTextureViewRelease(texView); wgpuTextureRelease(tex); wgpuShaderModuleRelease(calibShader); TracyWebGPUPanic("Failed to create calibration pipeline.", return false); }
// borrow query set 0 for calibration (no ticket needed)
uint32_t queryId = 0; uint32_t queryId = 0;
WGPUPassTimestampWrites anchorTs = {}; WGPUPassTimestampWrites anchorTs = {};
anchorTs.querySet = m_querySet; anchorTs.querySet = m_querySets[0];
anchorTs.beginningOfPassWriteIndex = queryId; anchorTs.beginningOfPassWriteIndex = queryId;
anchorTs.endOfPassWriteIndex = queryId+1; anchorTs.endOfPassWriteIndex = queryId+1;
@@ -333,7 +435,7 @@ namespace tracy
WGPUBuffer readBackBuffer = m_readbackReel[0].buffer; WGPUBuffer readBackBuffer = m_readbackReel[0].buffer;
uint32_t byteOffset = queryId * sizeof(uint64_t); uint32_t byteOffset = queryId * sizeof(uint64_t);
uint32_t sizeInBytes = 2 * sizeof(uint64_t); uint32_t sizeInBytes = 2 * sizeof(uint64_t);
wgpuCommandEncoderResolveQuerySet(enc, m_querySet, queryId, 2, m_resolveBuffer, byteOffset); wgpuCommandEncoderResolveQuerySet(enc, m_querySets[0], queryId, 2, m_resolveBuffer, byteOffset);
wgpuCommandEncoderCopyBufferToBuffer(enc, m_resolveBuffer, byteOffset, readBackBuffer, byteOffset, sizeInBytes); wgpuCommandEncoderCopyBufferToBuffer(enc, m_resolveBuffer, byteOffset, readBackBuffer, byteOffset, sizeInBytes);
WGPUCommandBuffer cmd = wgpuCommandEncoderFinish(enc, nullptr); WGPUCommandBuffer cmd = wgpuCommandEncoderFinish(enc, nullptr);
@@ -485,32 +587,42 @@ namespace tracy
TracyWebGPUAssert(device); wgpuDeviceAddRef(device); m_device = device; TracyWebGPUAssert(device); wgpuDeviceAddRef(device); m_device = device;
TracyWebGPUAssert(queue); wgpuQueueAddRef(queue); m_queue = queue; TracyWebGPUAssert(queue); wgpuQueueAddRef(queue); m_queue = queue;
// Setup Query Set: must have even size since queries are issued in pairs. // The WebGPU spec mandates a query set ceiling of 4096 queries,
// (The WebGPU spec mandates 4096, with no way to query the device limit.) // with no way to query the actual device limit.
// https://www.w3.org/TR/webgpu/#dom-gpudevice-createqueryset
// "descriptor.count must be <= 4096."
// For robustness, we attempt to negotiate it down from 4096 to 1024.
WGPUQuerySetDescriptor qsDesc = {}; WGPUQuerySetDescriptor qsDesc = {};
qsDesc.type = WGPUQueryType_Timestamp; qsDesc.type = WGPUQueryType_Timestamp;
qsDesc.count = 4096; qsDesc.count = 4096;
for (;;) WGPUQuerySet initialQuerySet = nullptr;
for (;; qsDesc.count /= 2)
{ {
m_querySet = wgpuDeviceCreateQuerySet(m_device, &qsDesc); if (qsDesc.count < 1024)
if (m_querySet) break; TracyWebGPUPanic("Failed to negotiate timestamp query set size.", return);
qsDesc.count /= 2; initialQuerySet = wgpuDeviceCreateQuerySet(m_device, &qsDesc);
if (qsDesc.count < 128) break; if (initialQuerySet != nullptr) break;
}
m_queriesPerSet = qsDesc.count;
*m_querySets.push_next() = initialQuerySet;
for (uint32_t total = qsDesc.count; total < QueryLimit; total += qsDesc.count)
{
WGPUQuerySet querySet = wgpuDeviceCreateQuerySet(m_device, &qsDesc);
if (querySet == nullptr)
TracyWebGPUPanic("Failed to create timestamp query set buffer.", return);
*m_querySets.push_next() = querySet;
} }
if (m_querySet == nullptr)
TracyWebGPUPanic("Failed to create timestamp query set.", return);
m_queryLimit = qsDesc.count;
WGPUBufferDescriptor resolveDesc = {}; WGPUBufferDescriptor resolveDesc = {};
resolveDesc.usage = WGPUBufferUsage_QueryResolve | WGPUBufferUsage_CopySrc; resolveDesc.usage = WGPUBufferUsage_QueryResolve | WGPUBufferUsage_CopySrc;
resolveDesc.size = static_cast<uint64_t>(m_queryLimit) * sizeof(uint64_t); resolveDesc.size = static_cast<uint64_t>(QueryLimit) * sizeof(uint64_t);
m_resolveBuffer = wgpuDeviceCreateBuffer(m_device, &resolveDesc); m_resolveBuffer = wgpuDeviceCreateBuffer(m_device, &resolveDesc);
if (!m_resolveBuffer) if (!m_resolveBuffer)
TracyWebGPUPanic("Failed to create timestamp resolve buffer.", return); TracyWebGPUPanic("Failed to create timestamp resolve buffer.", return);
WGPUBufferDescriptor readbackDesc = {}; WGPUBufferDescriptor readbackDesc = {};
readbackDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead; readbackDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead;
readbackDesc.size = static_cast<uint64_t>(m_queryLimit) * sizeof(uint64_t); readbackDesc.size = static_cast<uint64_t>(QueryLimit) * sizeof(uint64_t);
for (auto& stage : m_readbackReel) for (auto& stage : m_readbackReel)
{ {
stage.buffer = wgpuDeviceCreateBuffer(m_device, &readbackDesc); stage.buffer = wgpuDeviceCreateBuffer(m_device, &readbackDesc);
@@ -525,22 +637,10 @@ namespace tracy
TracyWebGPUPanic("Failed to calibrate CPU/GPU clocks.", return); TracyWebGPUPanic("Failed to calibrate CPU/GPU clocks.", return);
TracyWebGPUDebug( fprintf(stdout, "[WebGPUQueueCtx] cpuTimestamp: %llu | gpuTimestamp: %llu | period: %f\n", cpuTimestamp, gpuTimestamp, period) ); TracyWebGPUDebug( fprintf(stdout, "[WebGPUQueueCtx] cpuTimestamp: %llu | gpuTimestamp: %llu | period: %f\n", cpuTimestamp, gpuTimestamp, period) );
m_shadowBuffer.resize(m_queryLimit, gpuTimestamp); for (size_t i = 0; i < QueryLimit; ++i) *m_shadowBuffer.push_next() = gpuTimestamp;
// All setup completed: register the context. // All setup completed: register the context.
m_contextId = NextGpuContextId(); m_contextId = TracyEmitter::EmitGpuNewContext(cpuTimestamp, gpuTimestamp, period);
ZoneValue(m_contextId);
auto* item = Profiler::QueueSerial();
MemWrite(&item->hdr.type, QueueType::GpuNewContext);
MemWrite(&item->gpuNewContext.cpuTime, static_cast<int64_t>(cpuTimestamp));
MemWrite(&item->gpuNewContext.gpuTime, static_cast<int64_t>(gpuTimestamp));
MemWrite(&item->gpuNewContext.thread, static_cast<uint32_t>(0));
MemWrite(&item->gpuNewContext.period, static_cast<float>(period));
MemWrite(&item->gpuNewContext.context, static_cast<uint8_t>(GetId()));
MemWrite(&item->gpuNewContext.flags, GpuContextFlags(0)); // no calibration available
MemWrite(&item->gpuNewContext.type, GpuContextType::WebGPU);
SubmitQueueItem(item);
} }
~WebGPUQueueCtx() ~WebGPUQueueCtx()
@@ -554,7 +654,8 @@ namespace tracy
for (auto& stage : m_readbackReel) for (auto& stage : m_readbackReel)
if (stage.buffer) { wgpuBufferRelease(stage.buffer); stage.buffer = nullptr; } if (stage.buffer) { wgpuBufferRelease(stage.buffer); stage.buffer = nullptr; }
if (m_resolveBuffer) { wgpuBufferRelease(m_resolveBuffer); m_resolveBuffer = nullptr; } if (m_resolveBuffer) { wgpuBufferRelease(m_resolveBuffer); m_resolveBuffer = nullptr; }
if (m_querySet) { wgpuQuerySetRelease(m_querySet); m_querySet = nullptr; } for (auto& querySet : m_querySets)
if (querySet) { wgpuQuerySetRelease(querySet); querySet = nullptr; }
if (m_queue) { wgpuQueueRelease(m_queue); m_queue = nullptr; } if (m_queue) { wgpuQueueRelease(m_queue); m_queue = nullptr; }
if (m_device) { wgpuDeviceRelease(m_device); m_device = nullptr; } if (m_device) { wgpuDeviceRelease(m_device); m_device = nullptr; }
if (m_instance) { wgpuInstanceRelease(m_instance); m_instance = nullptr; } if (m_instance) { wgpuInstanceRelease(m_instance); m_instance = nullptr; }
@@ -567,15 +668,7 @@ namespace tracy
void Name(const char* name, uint16_t len) void Name(const char* name, uint16_t len)
{ {
auto ptr = (char*)tracy_malloc(len); TracyEmitter::EmitGpuContextName(GetId(), name, len);
memcpy(ptr, name, len);
auto item = Profiler::QueueSerial();
MemWrite(&item->hdr.type, QueueType::GpuContextName);
MemWrite(&item->gpuContextNameFat.context, static_cast<uint8_t>(GetId()));
MemWrite(&item->gpuContextNameFat.ptr, (uint64_t)ptr);
MemWrite(&item->gpuContextNameFat.size, len);
SubmitQueueItem(item);
} }
void Collect(bool webgpuProcessEvents=false) void Collect(bool webgpuProcessEvents=false)
@@ -614,7 +707,7 @@ namespace tracy
{ {
const uint64_t* ts = static_cast<const uint64_t*>( const uint64_t* ts = static_cast<const uint64_t*>(
wgpuBufferGetConstMappedRange(collectStage.buffer, 0, wgpuBufferGetConstMappedRange(collectStage.buffer, 0,
static_cast<uint64_t>(m_queryLimit) * sizeof(uint64_t))); static_cast<uint64_t>(QueryLimit) * sizeof(uint64_t)));
if (ts) if (ts)
{ {
uint64_t ticket = m_previousCheckpoint; uint64_t ticket = m_previousCheckpoint;
@@ -672,28 +765,32 @@ namespace tracy
cbInfo.userdata1 = &nextToCollect; cbInfo.userdata1 = &nextToCollect;
nextToCollect.pendingFuture = wgpuBufferMapAsync( nextToCollect.pendingFuture = wgpuBufferMapAsync(
nextToCollect.buffer, WGPUMapMode_Read, 0, nextToCollect.buffer, WGPUMapMode_Read, 0,
static_cast<uint64_t>(m_queryLimit) * sizeof(uint64_t), cbInfo); static_cast<uint64_t>(QueryLimit) * sizeof(uint64_t), cbInfo);
} }
private: private:
void EmitGpuTime(uint64_t gpuTimestamp, uint32_t queryId) void EmitGpuTime(uint64_t gpuTimestamp, uint32_t queryId)
{ {
auto* item = Profiler::QueueSerial(); TracyEmitter::EmitGpuTime(GetId(), gpuTimestamp, queryId);
MemWrite(&item->hdr.type, QueueType::GpuTime);
MemWrite(&item->gpuTime.gpuTime, static_cast<int64_t>(gpuTimestamp));
MemWrite(&item->gpuTime.queryId, static_cast<uint16_t>(queryId));
MemWrite(&item->gpuTime.context, static_cast<uint8_t>(GetId()));
Profiler::QueueSerialFinish();
m_shadowBuffer[queryId] = gpuTimestamp; m_shadowBuffer[queryId] = gpuTimestamp;
} }
tracy_force_inline uint32_t RingCapacity() const { return m_queryLimit; } tracy_force_inline uint32_t RingCapacity() const { return QueryLimit; }
tracy_force_inline uint32_t RingIndex(uint64_t t) const tracy_force_inline uint32_t RingIndex(uint64_t t) const
{ {
return static_cast<uint32_t>(t % RingCapacity()); return static_cast<uint32_t>(t % RingCapacity());
} }
tracy_force_inline WGPUQuerySet QuerySetForSlot(uint32_t slot) const
{
return m_querySets[slot / m_queriesPerSet];
}
tracy_force_inline uint32_t LocalSlot(uint32_t slot) const
{
return slot & (m_queriesPerSet - 1);
}
tracy_force_inline static int64_t Distance(uint64_t begin, uint64_t end) tracy_force_inline static int64_t Distance(uint64_t begin, uint64_t end)
{ {
return static_cast<int64_t>(end - begin); return static_cast<int64_t>(end - begin);
@@ -733,13 +830,13 @@ namespace tracy
// 32 queries = 32 * 8 bytes = 256 bytes // 32 queries = 32 * 8 bytes = 256 bytes
TracyWebGPUAssert(queryBatchStartId % 32 == 0, return); TracyWebGPUAssert(queryBatchStartId % 32 == 0, return);
queryBatchStartId = m_ctx->RingIndex(queryBatchStartId); const uint32_t globalSlot = m_ctx->RingIndex(queryBatchStartId);
const uint64_t blockOffset = static_cast<uint64_t>(queryBatchStartId) * sizeof(uint64_t); const uint64_t blockOffset = static_cast<uint64_t>(globalSlot) * sizeof(uint64_t);
wgpuCommandEncoderResolveQuerySet( wgpuCommandEncoderResolveQuerySet(
m_encoder, m_encoder,
m_ctx->m_querySet, m_ctx->QuerySetForSlot(globalSlot),
queryBatchStartId, 32, m_ctx->LocalSlot(globalSlot), 32,
m_ctx->m_resolveBuffer, m_ctx->m_resolveBuffer,
blockOffset // MUST be a multiple of (aligned to) 256... blockOffset // MUST be a multiple of (aligned to) 256...
); );
@@ -762,54 +859,7 @@ namespace tracy
uint64_t prev = stage.copiedUpto; uint64_t prev = stage.copiedUpto;
while ((WebGPUQueueCtx::Distance(prev, blockEnd) > 0) && while ((WebGPUQueueCtx::Distance(prev, blockEnd) > 0) &&
!stage.copiedUpto.compare_exchange_weak(prev, blockEnd)) {} !stage.copiedUpto.compare_exchange_weak(prev, blockEnd)) {}
TracyWebGPUDebug( fprintf(stdout, "[TWG] WebGPUZoneScope [%d] (%d,%d)\n", (int)m_ctx->m_writeIdx, queryBatchStartId, queryBatchStartId+32) ); TracyWebGPUDebug( fprintf(stdout, "[TWG] WebGPUZoneScope [%d] (%u,%u)\n", (int)m_ctx->m_writeIdx, globalSlot, globalSlot+32) );
}
tracy_force_inline void WriteQueueItem(const SourceLocationData* srcLocation, int32_t callstackDepth, uint32_t sourceLine, const char* sourceFile, size_t sourceFileLen, const char* functionName, size_t functionNameLen, const char* zoneName, size_t zoneNameLen)
{
if (!m_active) return;
const bool captureCallstack = callstackDepth > 0 && has_callstack();
const bool transientZone = srcLocation == nullptr;
uint64_t srcLocationAddr = reinterpret_cast<uint64_t>(srcLocation);
QueueItem* item = nullptr;
QueueType itemType;
if (transientZone)
{
srcLocationAddr = Profiler::AllocSourceLocation(sourceLine, sourceFile, sourceFileLen, functionName, functionNameLen, zoneName, zoneNameLen);
if (captureCallstack)
{
item = Profiler::QueueSerialCallstack(Callstack(callstackDepth));
itemType = QueueType::GpuZoneBeginAllocSrcLocCallstackSerial;
}
else
{
item = Profiler::QueueSerial();
itemType = QueueType::GpuZoneBeginAllocSrcLocSerial;
}
}
else
{
if (captureCallstack)
{
item = Profiler::QueueSerialCallstack(Callstack(callstackDepth));
itemType = QueueType::GpuZoneBeginCallstackSerial;
}
else
{
item = Profiler::QueueSerial();
itemType = QueueType::GpuZoneBeginSerial;
}
}
MemWrite(&item->hdr.type, itemType);
MemWrite(&item->gpuZoneBegin.cpuTime, Profiler::GetTime());
MemWrite(&item->gpuZoneBegin.srcloc, srcLocationAddr);
MemWrite(&item->gpuZoneBegin.thread, GetThreadHandle());
MemWrite(&item->gpuZoneBegin.queryId, static_cast<uint16_t>(m_queryId));
MemWrite(&item->gpuZoneBegin.context, static_cast<uint8_t>(m_ctx->GetId()));
Profiler::QueueSerialFinish();
} }
// Fills in m_timestampWrites and assigns its address to passDesc.timestampWrites. // Fills in m_timestampWrites and assigns its address to passDesc.timestampWrites.
@@ -823,9 +873,10 @@ namespace tracy
m_rawTicket = m_ctx->NextQueryId(); m_rawTicket = m_ctx->NextQueryId();
m_queryId = m_ctx->RingIndex(m_rawTicket); m_queryId = m_ctx->RingIndex(m_rawTicket);
m_timestampWrites.querySet = m_ctx->m_querySet; const uint32_t localSlot = m_ctx->LocalSlot(m_queryId);
m_timestampWrites.beginningOfPassWriteIndex = m_queryId; m_timestampWrites.querySet = m_ctx->QuerySetForSlot(m_queryId);
m_timestampWrites.endOfPassWriteIndex = m_queryId + 1; m_timestampWrites.beginningOfPassWriteIndex = localSlot;
m_timestampWrites.endOfPassWriteIndex = localSlot + 1;
passDesc.timestampWrites = &m_timestampWrites; passDesc.timestampWrites = &m_timestampWrites;
} }
@@ -841,7 +892,7 @@ namespace tracy
{ {
if (!m_active || !ctx) return; if (!m_active || !ctx) return;
InitBase(ctx, encoder, passDesc); InitBase(ctx, encoder, passDesc);
WriteQueueItem(srcLocation, 0, 0, nullptr, 0, nullptr, 0, nullptr, 0); TracyEmitter::EmitGpuZoneBegin(m_ctx->GetId(), m_queryId, srcLocation, 0, 0, nullptr, 0, nullptr, 0, nullptr, 0);
} }
template<typename PassDescriptor> template<typename PassDescriptor>
@@ -855,7 +906,7 @@ namespace tracy
{ {
if (!m_active || !ctx) return; if (!m_active || !ctx) return;
InitBase(ctx, encoder, passDesc); InitBase(ctx, encoder, passDesc);
WriteQueueItem(srcLocation, depth, 0, nullptr, 0, nullptr, 0, nullptr, 0); TracyEmitter::EmitGpuZoneBegin(m_ctx->GetId(), m_queryId, srcLocation, depth, 0, nullptr, 0, nullptr, 0, nullptr, 0);
} }
template<typename PassDescriptor> template<typename PassDescriptor>
@@ -869,7 +920,7 @@ namespace tracy
{ {
if (!m_active || !ctx) return; if (!m_active || !ctx) return;
InitBase(ctx, encoder, passDesc); InitBase(ctx, encoder, passDesc);
WriteQueueItem(nullptr, 0, line, source, sourceSz, function, functionSz, name, nameSz); TracyEmitter::EmitGpuZoneBegin(m_ctx->GetId(), m_queryId, nullptr, 0, line, source, sourceSz, function, functionSz, name, nameSz);
} }
template<typename PassDescriptor> template<typename PassDescriptor>
@@ -883,7 +934,7 @@ namespace tracy
{ {
if (!m_active || !ctx) return; if (!m_active || !ctx) return;
InitBase(ctx, encoder, passDesc); InitBase(ctx, encoder, passDesc);
WriteQueueItem(nullptr, depth, line, source, sourceSz, function, functionSz, name, nameSz); TracyEmitter::EmitGpuZoneBegin(m_ctx->GetId(), m_queryId, nullptr, depth, line, source, sourceSz, function, functionSz, name, nameSz);
} }
tracy_force_inline ~WebGPUZoneScope() tracy_force_inline ~WebGPUZoneScope()
@@ -894,18 +945,8 @@ namespace tracy
#ifdef TRACY_ON_DEMAND #ifdef TRACY_ON_DEMAND
if (GetProfiler().ConnectionId() == m_connectionId) if (GetProfiler().ConnectionId() == m_connectionId)
{
#endif
auto* item = Profiler::QueueSerial();
MemWrite(&item->hdr.type, QueueType::GpuZoneEndSerial);
MemWrite(&item->gpuZoneEnd.cpuTime, Profiler::GetTime());
MemWrite(&item->gpuZoneEnd.thread, GetThreadHandle());
MemWrite(&item->gpuZoneEnd.queryId, static_cast<uint16_t>(queryId));
MemWrite(&item->gpuZoneEnd.context, static_cast<uint8_t>(m_ctx->GetId()));
Profiler::QueueSerialFinish();
#ifdef TRACY_ON_DEMAND
}
#endif #endif
TracyEmitter::EmitGpuZoneEnd(m_ctx->GetId(), queryId);
if (m_queryId % 32 == 0) if (m_queryId % 32 == 0)
ResolveQueryBatch(m_queryId-32); ResolveQueryBatch(m_queryId-32);

View File

@@ -372,15 +372,6 @@ struct LockMap
TimeRange range[64]; TimeRange range[64];
}; };
struct LockHighlight
{
int64_t id;
int64_t begin;
int64_t end;
uint8_t thread;
bool blocked;
};
struct GpuEvent struct GpuEvent
{ {

View File

@@ -98,6 +98,12 @@ private:
class FileRead class FileRead
{ {
#ifdef __EMSCRIPTEN__
static constexpr int ThreadedStreams = 4;
#else
static constexpr int ThreadedStreams = 256;
#endif
struct StreamHandle struct StreamHandle
{ {
StreamHandle( uint8_t type ) : stream( type ), outputReady( false ) {} StreamHandle( uint8_t type ) : stream( type ), outputReady( false ) {}
@@ -114,6 +120,7 @@ class FileRead
std::condition_variable signal; std::condition_variable signal;
std::thread thread; std::thread thread;
bool threaded = true;
}; };
public: public:
@@ -131,7 +138,7 @@ public:
v->exit = true; v->exit = true;
v->signal.notify_one(); v->signal.notify_one();
} }
for( auto& v : m_streams ) v->thread.join(); for( auto& v : m_streams ) if( v->threaded ) v->thread.join();
m_streams.clear(); m_streams.clear();
if( m_data ) munmap( m_data, m_dataSize ); if( m_data ) munmap( m_data, m_dataSize );
} }
@@ -511,7 +518,11 @@ private:
uptr->src = m_data + m_dataOffset; uptr->src = m_data + m_dataOffset;
uptr->size = sz; uptr->size = sz;
uptr->inputReady = true; uptr->inputReady = true;
uptr->thread = std::thread( [ptr = uptr.get()] { Worker( ptr ); } ); uptr->threaded = i < ThreadedStreams;
if( uptr->threaded )
{
uptr->thread = std::thread( [ptr = uptr.get()] { Worker( ptr ); } );
}
m_streams.emplace_back( std::move( uptr ) ); m_streams.emplace_back( std::move( uptr ) );
m_dataOffset += sz; m_dataOffset += sz;
} }
@@ -595,8 +606,16 @@ private:
void GetNextDataBlock() void GetNextDataBlock()
{ {
auto& hnd = *m_streams[m_streamId]; auto& hnd = *m_streams[m_streamId];
while( hnd.outputReady.load( std::memory_order_acquire ) == false ) { YieldThread(); } if( hnd.threaded )
hnd.outputReady.store( false, std::memory_order_relaxed ); {
while( hnd.outputReady.load( std::memory_order_acquire ) == false ) { YieldThread(); }
hnd.outputReady.store( false, std::memory_order_relaxed );
}
else if( hnd.inputReady )
{
hnd.stream.Decompress( hnd.src, hnd.size );
hnd.inputReady = false;
}
m_buf = hnd.stream.GetBuffer(); m_buf = hnd.stream.GetBuffer();
m_offset = 0; m_offset = 0;
@@ -607,7 +626,7 @@ private:
hnd.src = m_data + m_dataOffset; hnd.src = m_data + m_dataOffset;
hnd.size = sz; hnd.size = sz;
hnd.inputReady = true; hnd.inputReady = true;
hnd.signal.notify_one(); if( hnd.threaded ) hnd.signal.notify_one();
lock.unlock(); lock.unlock();
m_dataOffset += sz; m_dataOffset += sz;
} }

View File

@@ -54,19 +54,30 @@ namespace EventType
struct UnsupportedVersion : public std::exception struct UnsupportedVersion : public std::exception
{ {
UnsupportedVersion( int version ) : version( version ) {} UnsupportedVersion( int version ) : version( version )
{
msg = "Trace file requires Tracy " + std::to_string( version >> 16 ) + "." + std::to_string( ( version >> 8 ) & 0xFF ) + "." + std::to_string( version & 0xFF ) + " or newer.";
}
const char* what() const noexcept override { return msg.c_str(); }
int version; int version;
std::string msg;
}; };
struct LegacyVersion : public std::exception struct LegacyVersion : public std::exception
{ {
LegacyVersion( int version ) : version ( version ) {} LegacyVersion( int version ) : version ( version )
{
msg = "Trace file was created by legacy Tracy " + std::to_string( version >> 16 ) + "." + std::to_string( ( version >> 8 ) & 0xFF ) + "." + std::to_string( version & 0xFF ) + ". Use the update utility from an older version to convert it.";
}
const char* what() const noexcept override { return msg.c_str(); }
int version; int version;
std::string msg;
}; };
struct LoadFailure : public std::exception struct LoadFailure : public std::exception
{ {
LoadFailure( const char* msg ) : msg( msg ) {} LoadFailure( const char* msg ) : msg( msg ) {}
const char* what() const noexcept override { return msg.c_str(); }
std::string msg; std::string msg;
}; };
@@ -231,7 +242,7 @@ private:
struct CallstackFrameIdHash struct CallstackFrameIdHash
{ {
size_t operator()( const CallstackFrameId& id ) const { return id.data; } uint64_t operator()( const CallstackFrameId& id ) const { return id.data; }
}; };
struct CallstackFrameIdCompare struct CallstackFrameIdCompare

View File

@@ -256,7 +256,7 @@ int main( int argc, char** argv )
} }
catch( const tracy::FileReadError& e ) catch( const tracy::FileReadError& e )
{ {
fprintf( stderr, "The file you are trying to open cannot be mapped to memory.\n" ); fprintf( stderr, "The file could not be read or is corrupted.\n" );
exit( 1 ); exit( 1 );
} }
catch( const tracy::LegacyVersion& e ) catch( const tracy::LegacyVersion& e )