1
0
mirror of https://github.com/wolfpld/tracy.git synced 2025-03-20 07:40:02 +08:00

Merge 198b732d8f290c52f89b59cd95ef27f4ae5f9499 into 38636648d3cae25431762a3e43e260e904f978db

This commit is contained in:
Dirk Eibach 2025-01-01 17:06:45 +01:00 committed by GitHub
commit 3deb7e4f9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1443 additions and 10 deletions

View File

@ -53,6 +53,7 @@ set(SERVER_FILES
TracyUtility.cpp
TracyView.cpp
TracyView_Annotations.cpp
TracyView_Blobs.cpp
TracyView_Callstack.cpp
TracyView_Compare.cpp
TracyView_ConnectionState.cpp

View File

@ -900,6 +900,8 @@ bool View::DrawImpl()
ImGui::SameLine();
ToggleButton( ICON_FA_TAGS " Messages", m_showMessages );
ImGui::SameLine();
ToggleButton( ICON_FA_CUBE " Blobs", m_showBlobs );
ImGui::SameLine();
ToggleButton( ICON_FA_MAGNIFYING_GLASS " Find", m_findZone.show );
ImGui::SameLine();
ToggleButton( ICON_FA_ARROW_UP_WIDE_SHORT " Statistics", m_showStatistics );
@ -1132,6 +1134,7 @@ bool View::DrawImpl()
if( m_showOptions ) DrawOptions();
if( m_showMessages ) DrawMessages();
if( m_showBlobs ) DrawBlobs();
if( m_showFlameGraph ) DrawFlameGraph();
if( m_findZone.show ) DrawFindZone();
if( m_showStatistics ) DrawStatistics();

View File

@ -253,6 +253,8 @@ private:
void DrawOptions();
void DrawMessages();
void DrawMessageLine( const MessageData& msg, bool hasCallstack, int& idx );
void DrawBlobs();
void DrawBlobLine( const BlobData& msg, bool hasCallstack, int& idx );
void DrawFindZone();
void AccumulationModeComboBox();
void DrawStatistics();
@ -388,6 +390,7 @@ private:
void UpdateTitle();
unordered_flat_map<uint64_t, bool> m_visibleMsgThread;
unordered_flat_map<uint64_t, bool> m_visibleBlobThread;
unordered_flat_map<uint64_t, bool> m_waitStackThread;
unordered_flat_map<uint64_t, bool> m_flameGraphThread;
unordered_flat_map<const void*, int> m_gpuDrift;
@ -406,6 +409,16 @@ private:
return it->second;
}
tracy_force_inline bool& VisibleBlobThread( uint64_t thread )
{
auto it = m_visibleBlobThread.find( thread );
if( it == m_visibleBlobThread.end() )
{
it = m_visibleBlobThread.emplace( thread, true ).first;
}
return it->second;
}
tracy_force_inline bool& WaitStackThread( uint64_t thread )
{
auto it = m_waitStackThread.find( thread );
@ -461,8 +474,11 @@ private:
LockHighlight m_lockHighlight { -1 };
LockHighlight m_nextLockHighlight;
DecayValue<const MessageData*> m_msgHighlight = nullptr;
DecayValue<const BlobData*> m_blobHighlight = nullptr;
DecayValue<uint32_t> m_lockHoverHighlight = InvalidId;
DecayValue<const MessageData*> m_msgToFocus = nullptr;
DecayValue<const BlobData*> m_blobToFocus = nullptr;
DecayValue<const BlobData*> m_blobSelected = nullptr;
const GpuEvent* m_gpuInfoWindow = nullptr;
const GpuEvent* m_gpuHighlight;
uint64_t m_gpuInfoWindowThread;
@ -479,11 +495,17 @@ private:
int m_frameHover = -1;
bool m_messagesScrollBottom;
ImGuiTextFilter m_messageFilter;
bool m_blobsScrollBottom;
ImGuiTextFilter m_blobFilter;
bool m_showMessageImages = false;
int m_visibleMessages = 0;
size_t m_prevMessages = 0;
bool m_messagesShowCallstack = false;
Vector<uint32_t> m_msgList;
int m_visibleBlobs = 0;
size_t m_prevBlobs = 0;
bool m_blobsShowCallstack = false;
Vector<uint32_t> m_blobList;
bool m_disconnectIssued = false;
DecayValue<uint64_t> m_drawThreadMigrations = 0;
DecayValue<uint64_t> m_drawThreadHighlight = 0;
@ -506,6 +528,7 @@ private:
bool m_showOptions = false;
bool m_showMessages = false;
bool m_showBlobs = false;
bool m_showStatistics = false;
bool m_showInfo = false;
bool m_showPlayback = false;

View File

@ -0,0 +1,306 @@
#include "TracyImGui.hpp"
#include "TracyPrint.hpp"
#include "TracyTexture.hpp"
#include "TracyView.hpp"
#include <cinttypes>
#include "imgui_memory_editor.h"
namespace tracy
{
void View::DrawBlobs()
{
const auto& blobs = m_worker.GetBlobs();
const auto scale = GetScale();
ImGui::SetNextWindowSize( ImVec2( 1200 * scale, 600 * scale ), ImGuiCond_FirstUseEver );
ImGui::Begin( "Blobs", &m_showBlobs );
if( ImGui::GetCurrentWindowRead()->SkipItems ) { ImGui::End(); return; }
if( blobs.empty() )
{
const auto ty = ImGui::GetTextLineHeight();
ImGui::PushFont( m_bigFont );
ImGui::Dummy( ImVec2( 0, ( ImGui::GetContentRegionAvail().y - ImGui::GetTextLineHeight() * 2 ) * 0.5f ) );
TextCentered( ICON_FA_FISH_FINS );
TextCentered( "No blobs were collected" );
ImGui::PopFont();
ImGui::End();
return;
}
ImGui::BeginChild( "Blobs View", ImVec2( 600 * scale, 0 ), ImGuiChildFlags_Borders );
bool filterChanged = m_blobFilter.Draw( ICON_FA_FILTER " Filter blobs", 200 );
ImGui::SameLine();
if( ImGui::Button( ICON_FA_DELETE_LEFT " Clear" ) )
{
m_blobFilter.Clear();
filterChanged = true;
}
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
TextFocused( "Total blob count:", RealToString( blobs.size() ) );
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
TextFocused( "Visible blobs:", RealToString( m_visibleBlobs ) );
bool threadsChanged = false;
auto expand = ImGui::TreeNode( ICON_FA_SHUFFLE " Visible threads:" );
ImGui::SameLine();
size_t visibleThreads = 0;
size_t tsz = 0;
for( const auto& t : m_threadOrder )
{
if( t->blobs.empty() ) continue;
if( VisibleBlobThread( t->id ) ) visibleThreads++;
tsz++;
}
if( visibleThreads == tsz )
{
ImGui::TextDisabled( "(%zu)", tsz );
}
else
{
ImGui::TextDisabled( "(%zu/%zu)", visibleThreads, tsz );
}
if( expand )
{
auto& crash = m_worker.GetCrashEvent();
ImGui::SameLine();
if( ImGui::SmallButton( "Select all" ) )
{
for( const auto& t : m_threadOrder )
{
VisibleBlobThread( t->id ) = true;
}
threadsChanged = true;
}
ImGui::SameLine();
if( ImGui::SmallButton( "Unselect all" ) )
{
for( const auto& t : m_threadOrder )
{
VisibleBlobThread( t->id ) = false;
}
threadsChanged = true;
}
int idx = 0;
for( const auto& t : m_threadOrder )
{
if( t->blobs.empty() ) continue;
ImGui::PushID( idx++ );
const auto threadColor = GetThreadColor( t->id, 0 );
SmallColorBox( threadColor );
ImGui::SameLine();
if( SmallCheckbox( m_worker.GetThreadName( t->id ), &VisibleBlobThread( t->id ) ) )
{
threadsChanged = true;
}
ImGui::PopID();
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( t->blobs.size() ) );
if( crash.thread == t->id )
{
ImGui::SameLine();
TextColoredUnformatted( ImVec4( 1.f, 0.2f, 0.2f, 1.f ), ICON_FA_SKULL " Crashed" );
}
if( t->isFiber )
{
ImGui::SameLine();
TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
}
}
ImGui::TreePop();
}
const bool blobsChanged = blobs.size() != m_prevBlobs;
if( filterChanged || threadsChanged )
{
bool showCallstack = false;
m_blobList.reserve( blobs.size() );
m_blobList.clear();
if( m_blobFilter.IsActive() )
{
for( size_t i=0; i<blobs.size(); i++ )
{
const auto& v = blobs[i];
const auto tid = m_worker.DecompressThread( v->thread );
if( VisibleBlobThread( tid ) )
{
const auto text = m_worker.GetString( blobs[i]->ref );
if( m_blobFilter.PassFilter( text ) )
{
if( !showCallstack && blobs[i]->callstack.Val() != 0 ) showCallstack = true;
m_blobList.push_back_no_space_check( uint32_t( i ) );
}
}
}
}
else
{
for( size_t i=0; i<blobs.size(); i++ )
{
const auto& v = blobs[i];
const auto tid = m_worker.DecompressThread( v->thread );
if( VisibleBlobThread( tid ) )
{
if( !showCallstack && blobs[i]->callstack.Val() != 0 ) showCallstack = true;
m_blobList.push_back_no_space_check( uint32_t( i ) );
}
}
}
m_blobsShowCallstack = showCallstack;
m_visibleBlobs = m_blobList.size();
if( blobsChanged ) m_prevBlobs = blobs.size();
}
else if( blobsChanged )
{
assert( m_prevBlobs < blobs.size() );
bool showCallstack = m_blobsShowCallstack;
m_blobList.reserve( blobs.size() );
if( m_blobFilter.IsActive() )
{
for( size_t i=m_prevBlobs; i<blobs.size(); i++ )
{
const auto& v = blobs[i];
const auto tid = m_worker.DecompressThread( v->thread );
if( VisibleBlobThread( tid ) )
{
const auto text = m_worker.GetString( blobs[i]->ref );
if( m_blobFilter.PassFilter( text ) )
{
if( !showCallstack && blobs[i]->callstack.Val() != 0 ) showCallstack = true;
m_blobList.push_back_no_space_check( uint32_t( i ) );
}
}
}
}
else
{
for( size_t i=m_prevBlobs; i<blobs.size(); i++ )
{
const auto& v = blobs[i];
const auto tid = m_worker.DecompressThread( v->thread );
if( VisibleBlobThread( tid ) )
{
if( !showCallstack && blobs[i]->callstack.Val() != 0 ) showCallstack = true;
m_blobList.push_back_no_space_check( uint32_t( i ) );
}
}
}
m_blobsShowCallstack = showCallstack;
m_visibleBlobs = m_blobList.size();
m_prevBlobs = blobs.size();
}
bool hasCallstack = m_blobsShowCallstack;
ImGui::Separator();
ImGui::BeginChild( "##blobs" );
const int colNum = hasCallstack ? 4 : 3;
if( ImGui::BeginTable( "##blobs", colNum, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Hideable ) )
{
ImGui::TableSetupScrollFreeze( 0, 1 );
ImGui::TableSetupColumn( "Time", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
ImGui::TableSetupColumn( "Thread" );
ImGui::TableSetupColumn( "Encoding" );
if( hasCallstack ) ImGui::TableSetupColumn( "Call stack" );
ImGui::TableHeadersRow();
int idx = 0;
if( m_blobToFocus )
{
{
}
}
else
{
ImGuiListClipper clipper;
clipper.Begin( m_blobList.size() );
while( clipper.Step() )
{
for( auto i=clipper.DisplayStart; i<clipper.DisplayEnd; i++ )
{
DrawBlobLine( *blobs[m_blobList[i]], hasCallstack, idx );
}
}
}
if( m_worker.IsConnected() && ImGui::GetScrollY() >= ImGui::GetScrollMaxY() )
{
ImGui::SetScrollHereY( 1.f );
}
ImGui::EndTable();
}
ImGui::EndChild();
ImGui::EndChild();
if( m_blobSelected )
{
ImGui::SameLine();
ImGui::BeginChild("##blob hexdump", ImVec2( 600 * scale, 0 ), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoScrollbar);
ImGui::TextDisabled( "Blob at %s", TimeToStringExact( m_blobSelected->time ) );
size_t sz;
const auto data = m_worker.GetData( m_blobSelected->ref, &sz );
static MemoryEditor mem_edit;
mem_edit.DrawContents((void*)data, sz, 0);
ImGui::EndChild();
}
ImGui::End();
}
void View::DrawBlobLine( const BlobData& blob, bool hasCallstack, int& idx )
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
const auto tid = m_worker.DecompressThread( blob.thread );
ImGui::PushID( &blob );
if( ImGui::Selectable( TimeToStringExact( blob.time ), m_blobHighlight == &blob, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap ) )
{
CenterAtTime( blob.time );
m_blobSelected = &blob;
}
if( m_blobToFocus == &blob )
{
ImGui::SetScrollHereY();
m_blobToFocus.Decay( nullptr );
m_blobsScrollBottom = false;
}
ImGui::PopID();
ImGui::TableNextColumn();
SmallColorBox( GetThreadColor( tid, 0 ) );
ImGui::SameLine();
if( m_worker.IsThreadFiber( tid ) )
{
TextColoredUnformatted( 0xFF88FF88, m_worker.GetThreadName( tid ) );
}
else
{
ImGui::TextUnformatted( m_worker.GetThreadName( tid ) );
}
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( tid ) );
ImGui::TableNextColumn();
ImGui::TextDisabled( "%" PRIu64, blob.encoding);
if( hasCallstack )
{
ImGui::TableNextColumn();
const auto cs = blob.callstack.Val();
if( cs != 0 )
{
SmallCallstackButton( ICON_FA_ALIGN_JUSTIFY, cs, idx );
ImGui::SameLine();
DrawCallstackCalls( cs, 6 );
}
}
}
}

View File

@ -0,0 +1,832 @@
// Mini memory editor for Dear ImGui (to embed in your game/tools)
// Get latest version at http://www.github.com/ocornut/imgui_club
// Licensed under The MIT License (MIT)
// Right-click anywhere to access the Options menu!
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
// The code assume a mono-space font for simplicity!
// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before calling this.
//
// Usage:
// // Create a window and draw memory editor inside it:
// static MemoryEditor mem_edit_1;
// static char data[0x10000];
// size_t data_size = 0x10000;
// mem_edit_1.DrawWindow("Memory Editor", data, data_size);
//
// Usage:
// // If you already have a window, use DrawContents() instead:
// static MemoryEditor mem_edit_2;
// ImGui::Begin("MyWindow")
// mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);
// ImGui::End();
//
// Changelog:
// - v0.10: initial version
// - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write.
// - v0.24 (2018/06/02): changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61).
// - v0.25 (2018/07/11): fixed wording: all occurrences of "Rows" renamed to "Columns".
// - v0.26 (2018/08/02): fixed clicking on hex region
// - v0.30 (2018/08/02): added data preview for common data types
// - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display [@samhocevar]
// - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char*
// - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting.
// - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble]
// - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69.
// - v0.36 (2020/05/05): minor tweaks, minor refactor.
// - v0.40 (2020/10/04): fix misuse of ImGuiListClipper API, broke with Dear ImGui 1.79. made cursor position appears on left-side of edit box. option popup appears on mouse release. fix MSVC warnings where _CRT_SECURE_NO_WARNINGS wasn't working in recent versions.
// - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled.
// - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out.
// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel]
// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width.
// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size.
// - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#34)
// - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87.
// - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents(). warning fixes. (#35)
// - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2])
// - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from highlighted selection. (#27) [@StrikerX3]
// added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3]
// fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight color when using data preview.
// *BREAKING* added UserData field passed to all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn. (#50) [@silverweed]
// - v0.56 (2024/11/04): fixed MouseHovered, MouseHoveredAddr not being set when hovering a byte being edited. (#54)
//
// TODO:
// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day.
// - PageUp/PageDown are not supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
#pragma once
#include <stdio.h> // sprintf, scanf
#include <stdint.h> // uint8_t, etc.
#if defined(_MSC_VER) || defined(_UCRT)
#define _PRISizeT "I"
#define ImSnprintf _snprintf
#else
#define _PRISizeT "z"
#define ImSnprintf snprintf
#endif
#if defined(_MSC_VER) || defined(_UCRT)
#pragma warning (push)
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
#endif
struct MemoryEditor
{
enum DataFormat
{
DataFormat_Bin = 0,
DataFormat_Dec = 1,
DataFormat_Hex = 2,
DataFormat_COUNT
};
// Settings
bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow().
bool ReadOnly; // = false // disable any editing.
int Cols; // = 16 // number of columns to display.
bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
bool OptShowDataPreview; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes.
bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
bool OptShowAscii; // = true // display ASCII representation on the right side.
bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color.
bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff".
int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols.
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets
ImU32 HighlightColor; // // background color of highlighted bytes.
// Function handlers
ImU8 (*ReadFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to read bytes.
void (*WriteFn)(ImU8* mem, size_t off, ImU8 d, void* user_data); // = 0 // optional handler to write bytes.
bool (*HighlightFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
ImU32 (*BgColorFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return custom background color of individual bytes.
void* UserData; // = NULL // user data forwarded to the function handlers
// Public read-only data
bool MouseHovered; // set when mouse is hovering a value.
size_t MouseHoveredAddr; // the address currently being hovered if MouseHovered is set.
// [Internal State]
bool ContentsWidthChanged;
size_t DataPreviewAddr;
size_t DataEditingAddr;
bool DataEditingTakeFocus;
char DataInputBuf[32];
char AddrInputBuf[32];
size_t GotoAddr;
size_t HighlightMin, HighlightMax;
int PreviewEndianness;
ImGuiDataType PreviewDataType;
MemoryEditor()
{
// Settings
Open = true;
ReadOnly = true;
Cols = 16;
OptShowOptions = true;
OptShowDataPreview = false;
OptShowHexII = false;
OptShowAscii = true;
OptGreyOutZeroes = true;
OptUpperCaseHex = true;
OptMidColsCount = 8;
OptAddrDigitsCount = 0;
OptFooterExtraHeight = 0.0f;
HighlightColor = IM_COL32(255, 255, 255, 50);
ReadFn = nullptr;
WriteFn = nullptr;
HighlightFn = nullptr;
BgColorFn = nullptr;
UserData = nullptr;
// State/Internals
ContentsWidthChanged = false;
DataPreviewAddr = DataEditingAddr = (size_t)-1;
DataEditingTakeFocus = false;
memset(DataInputBuf, 0, sizeof(DataInputBuf));
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
GotoAddr = (size_t)-1;
MouseHovered = false;
MouseHoveredAddr = 0;
HighlightMin = HighlightMax = (size_t)-1;
PreviewEndianness = 0;
PreviewDataType = ImGuiDataType_S32;
}
void GotoAddrAndHighlight(size_t addr_min, size_t addr_max)
{
GotoAddr = addr_min;
HighlightMin = addr_min;
HighlightMax = addr_max;
}
struct Sizes
{
int AddrDigitsCount;
float LineHeight;
float GlyphWidth;
float HexCellWidth;
float SpacingBetweenMidCols;
float PosHexStart;
float PosHexEnd;
float PosAsciiStart;
float PosAsciiEnd;
float WindowWidth;
Sizes() { memset(this, 0, sizeof(*this)); }
};
Sizes* getSizes()
{
return new Sizes;
}
void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr)
{
ImGuiStyle& style = ImGui::GetStyle();
s.AddrDigitsCount = OptAddrDigitsCount;
if (s.AddrDigitsCount == 0)
for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4)
s.AddrDigitsCount++;
s.LineHeight = ImGui::GetTextLineHeight();
s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space
s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere
s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing
s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
if (OptShowAscii)
{
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
if (OptMidColsCount > 0)
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
}
s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
}
// Standalone Memory Editor window
void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)
{
Sizes s;
CalcSizes(s, mem_size, base_display_addr);
ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
Open = true;
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
{
DrawContents(mem_data, mem_size, base_display_addr);
if (ContentsWidthChanged)
{
CalcSizes(s, mem_size, base_display_addr);
ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y));
}
}
ImGui::End();
}
// Memory Editor contents only
void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000)
{
if (Cols < 1)
Cols = 1;
ImU8* mem_data = (ImU8*)mem_data_void;
Sizes s;
CalcSizes(s, mem_size, base_display_addr);
ImGuiStyle& style = ImGui::GetStyle();
const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos();
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
const float height_separator = style.ItemSpacing.y;
float footer_height = OptFooterExtraHeight;
if (OptShowOptions)
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
if (OptShowDataPreview)
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
ImGui::BeginChild("##scrolling", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
// We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function.
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
ImGuiListClipper clipper;
clipper.Begin(line_total_count, s.LineHeight);
bool data_next = false;
if (DataEditingAddr >= mem_size)
DataEditingAddr = (size_t)-1;
if (DataPreviewAddr >= mem_size)
DataPreviewAddr = (size_t)-1;
size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0;
size_t data_editing_addr_next = (size_t)-1;
if (DataEditingAddr != (size_t)-1)
{
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; }
else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols){ data_editing_addr_next = DataEditingAddr + Cols; }
else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; }
else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
}
// Draw vertical separator
ImVec2 window_pos = ImGui::GetWindowPos();
if (OptShowAscii)
draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
const char* format_address = OptUpperCaseHex ? "%0*" _PRISizeT "X: " : "%0*" _PRISizeT "x: ";
const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x";
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
MouseHovered = false;
MouseHoveredAddr = 0;
while (clipper.Step())
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
{
size_t addr = (size_t)line_i * Cols;
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
// Draw Hexadecimal
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
{
float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
if (OptMidColsCount > 0)
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
ImGui::SameLine(byte_pos_x);
// Draw highlight or custom background color
const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData));
const bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
ImU32 bg_color = 0;
bool is_next_byte_highlighted = false;
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
{
is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, UserData)) || (addr + 1 < DataPreviewAddr + preview_data_type_size));
bg_color = HighlightColor;
}
else if (BgColorFn != nullptr)
{
is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0);
bg_color = BgColorFn(mem_data, addr, UserData);
}
if (bg_color != 0)
{
float bg_width = s.GlyphWidth * 2;
if (is_next_byte_highlighted || (n + 1 == Cols))
{
bg_width = s.HexCellWidth;
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
bg_width += s.SpacingBetweenMidCols;
}
ImVec2 pos = ImGui::GetCursorScreenPos();
draw_list->AddRectFilled(pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color);
}
if (DataEditingAddr == addr)
{
// Display text input on current byte
bool data_write = false;
ImGui::PushID((void*)addr);
if (DataEditingTakeFocus)
{
ImGui::SetKeyboardFocusHere(0);
ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr);
ImSnprintf(DataInputBuf, 32, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
}
struct InputTextUserData
{
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
static int Callback(ImGuiInputTextCallbackData* data)
{
InputTextUserData* user_data = (InputTextUserData*)data->UserData;
if (!data->HasSelection())
user_data->CursorPos = data->CursorPos;
#if IMGUI_VERSION_NUM < 19102
if (data->Flags & ImGuiInputTextFlags_ReadOnly)
return 0;
#endif
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
{
// When not editing a byte, always refresh its InputText content pulled from underlying memory data
// (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there)
data->DeleteChars(0, data->BufTextLen);
data->InsertChars(0, user_data->CurrentBufOverwrite);
data->SelectionStart = 0;
data->SelectionEnd = 2;
data->CursorPos = 0;
}
return 0;
}
char CurrentBufOverwrite[3]; // Input
int CursorPos; // Output
};
InputTextUserData input_text_user_data;
input_text_user_data.CursorPos = -1;
ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways;
if (ReadOnly)
flags |= ImGuiInputTextFlags_ReadOnly;
flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode
ImGui::SetNextItemWidth(s.GlyphWidth * 2);
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, InputTextUserData::Callback, &input_text_user_data))
data_write = data_next = true;
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
DataEditingAddr = data_editing_addr_next = (size_t)-1;
DataEditingTakeFocus = false;
if (input_text_user_data.CursorPos >= 2)
data_write = data_next = true;
if (data_editing_addr_next != (size_t)-1)
data_write = data_next = false;
unsigned int data_input_value = 0;
if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
{
if (WriteFn)
WriteFn(mem_data, addr, (ImU8)data_input_value, UserData);
else
mem_data[addr] = (ImU8)data_input_value;
}
if (ImGui::IsItemHovered())
{
MouseHovered = true;
MouseHoveredAddr = addr;
}
ImGui::PopID();
}
else
{
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
if (OptShowHexII)
{
if ((b >= 32 && b < 128))
ImGui::Text(".%c ", b);
else if (b == 0xFF && OptGreyOutZeroes)
ImGui::TextDisabled("## ");
else if (b == 0x00)
ImGui::Text(" ");
else
ImGui::Text(format_byte_space, b);
}
else
{
if (b == 0 && OptGreyOutZeroes)
ImGui::TextDisabled("00 ");
else
ImGui::Text(format_byte_space, b);
}
if (ImGui::IsItemHovered())
{
MouseHovered = true;
MouseHoveredAddr = addr;
if (ImGui::IsMouseClicked(0))
{
DataEditingTakeFocus = true;
data_editing_addr_next = addr;
}
}
}
}
if (OptShowAscii)
{
// Draw ASCII values
ImGui::SameLine(s.PosAsciiStart);
ImVec2 pos = ImGui::GetCursorScreenPos();
addr = (size_t)line_i * Cols;
const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x;
const size_t mouse_addr = (mouse_off_x >= 0.0f && mouse_off_x < s.PosAsciiEnd - s.PosAsciiStart) ? addr + (size_t)(mouse_off_x / s.GlyphWidth) : (size_t)-1;
ImGui::PushID(line_i);
if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight)))
{
DataEditingAddr = DataPreviewAddr = mouse_addr;
DataEditingTakeFocus = true;
}
if (ImGui::IsItemHovered())
{
MouseHovered = true;
MouseHoveredAddr = mouse_addr;
}
ImGui::PopID();
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
{
if (addr == DataEditingAddr)
{
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
}
else if (BgColorFn)
{
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, UserData));
}
unsigned char c = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
char display_c = (c < 32 || c >= 128) ? '.' : c;
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
pos.x += s.GlyphWidth;
}
}
}
ImGui::PopStyleVar(2);
const float child_width = ImGui::GetWindowSize().x;
ImGui::EndChild();
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
ImGui::SetCursorPosX(s.WindowWidth);
ImGui::Dummy(ImVec2(0.0f, 0.0f));
if (data_next && DataEditingAddr + 1 < mem_size)
{
DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1;
DataEditingTakeFocus = true;
}
else if (data_editing_addr_next != (size_t)-1)
{
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
DataEditingTakeFocus = true;
}
const bool lock_show_data_preview = OptShowDataPreview;
if (OptShowOptions)
{
ImGui::Separator();
DrawOptionsLine(s, mem_data, mem_size, base_display_addr);
}
if (lock_show_data_preview)
{
ImGui::Separator();
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
}
const ImVec2 contents_pos_end(contents_pos_start.x + child_width, ImGui::GetCursorScreenPos().y);
//ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end, IM_COL32(255, 0, 0, 255));
if (OptShowOptions)
if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end))
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
ImGui::OpenPopup("OptionsPopup");
if (ImGui::BeginPopup("OptionsPopup"))
{
ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
ImGui::Checkbox("Show Data Preview", &OptShowDataPreview);
ImGui::Checkbox("Show HexII", &OptShowHexII);
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; }
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex);
ImGui::EndPopup();
}
}
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
{
IM_UNUSED(mem_data);
ImGuiStyle& style = ImGui::GetStyle();
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
// Options menu
if (ImGui::Button("Options"))
ImGui::OpenPopup("OptionsPopup");
ImGui::SameLine();
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
ImGui::SameLine();
ImGui::SetNextItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f);
if (ImGui::InputText("##addr", AddrInputBuf, IM_ARRAYSIZE(AddrInputBuf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue))
{
size_t goto_addr;
if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1)
{
GotoAddr = goto_addr - base_display_addr;
HighlightMin = HighlightMax = (size_t)-1;
}
}
if (GotoAddr != (size_t)-1)
{
if (GotoAddr < mem_size)
{
ImGui::BeginChild("##scrolling");
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight());
ImGui::EndChild();
DataEditingAddr = DataPreviewAddr = GotoAddr;
DataEditingTakeFocus = true;
}
GotoAddr = (size_t)-1;
}
//if (MouseHovered)
//{
// ImGui::SameLine();
// ImGui::Text("Hovered: %p", MouseHoveredAddr);
//}
}
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
{
IM_UNUSED(base_display_addr);
ImU8* mem_data = (ImU8*)mem_data_void;
ImGuiStyle& style = ImGui::GetStyle();
ImGui::AlignTextToFramePadding();
ImGui::Text("Preview as:");
ImGui::SameLine();
ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
static const ImGuiDataType supported_data_types[] = { ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16, ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64, ImGuiDataType_Float, ImGuiDataType_Double };
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
{
for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++)
{
ImGuiDataType data_type = supported_data_types[n];
if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type))
PreviewDataType = data_type;
}
ImGui::EndCombo();
}
ImGui::SameLine();
ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
ImGui::Combo("##combo_endianness", &PreviewEndianness, "LE\0BE\0\0");
char buf[128] = "";
float x = s.GlyphWidth * 6.0f;
bool has_value = DataPreviewAddr != (size_t)-1;
if (has_value)
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf));
ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
if (has_value)
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf));
ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
if (has_value)
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf));
buf[IM_ARRAYSIZE(buf) - 1] = 0;
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
}
// Utilities for Data Preview (since we don't access imgui_internal.h)
// FIXME: This technically depends on ImGuiDataType order.
const char* DataTypeGetDesc(ImGuiDataType data_type) const
{
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs));
return descs[data_type];
}
size_t DataTypeGetSize(ImGuiDataType data_type) const
{
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes));
return sizes[data_type];
}
const char* DataFormatGetDesc(DataFormat data_format) const
{
const char* descs[] = { "Bin", "Dec", "Hex" };
IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT);
return descs[data_format];
}
bool IsBigEndian() const
{
uint16_t x = 1;
char c[2];
memcpy(c, &x, 2);
return c[0] != 0;
}
static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)
{
if (is_little_endian)
{
uint8_t* dst = (uint8_t*)_dst;
uint8_t* src = (uint8_t*)_src + s - 1;
for (int i = 0, n = (int)s; i < n; ++i)
memcpy(dst++, src--, 1);
return _dst;
}
else
{
return memcpy(_dst, _src, s);
}
}
static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)
{
if (is_little_endian)
{
return memcpy(_dst, _src, s);
}
else
{
uint8_t* dst = (uint8_t*)_dst;
uint8_t* src = (uint8_t*)_src + s - 1;
for (int i = 0, n = (int)s; i < n; ++i)
memcpy(dst++, src--, 1);
return _dst;
}
}
void* EndiannessCopy(void* dst, void* src, size_t size) const
{
static void* (*fp)(void*, void*, size_t, int) = nullptr;
if (fp == nullptr)
fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian;
return fp(dst, src, size, PreviewEndianness);
}
const char* FormatBinary(const uint8_t* buf, int width) const
{
IM_ASSERT(width <= 64);
size_t out_n = 0;
static char out_buf[64 + 8 + 1];
int n = width / 8;
for (int j = n - 1; j >= 0; --j)
{
for (int i = 0; i < 8; ++i)
out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0';
out_buf[out_n++] = ' ';
}
IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf));
out_buf[out_n] = 0;
return out_buf;
}
// [Internal]
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
{
uint8_t buf[8];
size_t elem_size = DataTypeGetSize(data_type);
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
if (ReadFn)
for (int i = 0, n = (int)size; i < n; ++i)
buf[i] = ReadFn(mem_data, addr + i, UserData);
else
memcpy(buf, mem_data + addr, size);
if (data_format == DataFormat_Bin)
{
uint8_t binbuf[8];
EndiannessCopy(binbuf, buf, size);
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
return;
}
out_buf[0] = 0;
switch (data_type)
{
case ImGuiDataType_S8:
{
int8_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF); return; }
break;
}
case ImGuiDataType_U8:
{
uint8_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF); return; }
break;
}
case ImGuiDataType_S16:
{
int16_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
break;
}
case ImGuiDataType_U16:
{
uint16_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
break;
}
case ImGuiDataType_S32:
{
int32_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
break;
}
case ImGuiDataType_U32:
{
uint32_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
break;
}
case ImGuiDataType_S64:
{
int64_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
break;
}
case ImGuiDataType_U64:
{
uint64_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
break;
}
case ImGuiDataType_Float:
{
float data = 0.0f;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
break;
}
case ImGuiDataType_Double:
{
double data = 0.0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
break;
}
default:
case ImGuiDataType_COUNT:
break;
} // Switch
IM_ASSERT(0); // Shouldn't reach
}
};
#undef _PRISizeT
#undef ImSnprintf
#ifdef _MSC_VER
#pragma warning (pop)
#endif

View File

@ -2277,6 +2277,11 @@ static void FreeAssociatedMemory( const QueueItem& item )
ptr = MemRead<uint64_t>( &item.messageFat.text );
tracy_free( (void*)ptr );
break;
case QueueType::Blob:
case QueueType::BlobCallstack:
ptr = MemRead<uint64_t>( &item.blobData.data );
tracy_free( (void*)ptr );
break;
case QueueType::ZoneBeginAllocSrcLoc:
case QueueType::ZoneBeginAllocSrcLocCallstack:
ptr = MemRead<uint64_t>( &item.zoneBegin.srcloc );
@ -2459,6 +2464,15 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token )
tracy_free_fast( (void*)ptr );
#endif
break;
case QueueType::Blob:
case QueueType::BlobCallstack:
{
ptr = MemRead<uint64_t>( &item->blobData.data );
auto size = MemRead<uint32_t>( &item->blobData.size );
SendBlob( (const char*)ptr, size );
tracy_free_fast( (void*)ptr );
break;
}
case QueueType::ZoneBeginAllocSrcLoc:
case QueueType::ZoneBeginAllocSrcLocCallstack:
{
@ -2985,6 +2999,16 @@ Profiler::DequeueStatus Profiler::DequeueSerial()
tracy_free_fast( (void*)ptr );
break;
}
case QueueType::Blob:
case QueueType::BlobCallstack:
{
ThreadCtxCheckSerial( blobDataThread );
ptr = MemRead<uint64_t>( &item->blobData.data );
uint32_t size = MemRead<uint32_t>( &item->blobData.size );
SendBlob( (const char*)ptr, size );
tracy_free_fast( (void*)ptr );
break;
}
case QueueType::Callstack:
{
ThreadCtxCheckSerial( callstackFatThread );
@ -3249,6 +3273,29 @@ void Profiler::SendLongString( uint64_t str, const char* ptr, size_t len, QueueT
AppendDataUnsafe( ptr, l32 );
}
void Profiler::SendBlob( const char* ptr, size_t len )
{
QueueItem item;
uint32_t offset = 0;
uint32_t full_size = len;
MemWrite( &item.hdr.type, QueueType::BlobFragment );
while (len)
{
const uint32_t max_fragment_size = TargetFrameSize - QueueDataSize[(int)QueueType::BlobFragment] - 3 * sizeof ( uint32_t );
uint32_t fragment_size = len > max_fragment_size ? max_fragment_size : len;
len -= fragment_size;
NeedDataSize( QueueDataSize[(int)QueueType::BlobFragment] + 2 * sizeof ( uint32_t ) + sizeof( fragment_size ) + fragment_size );
AppendDataUnsafe( &item, QueueDataSize[(int)QueueType::BlobFragment] );
AppendDataUnsafe( &full_size, sizeof(uint32_t) );
AppendDataUnsafe( &offset, sizeof(uint32_t) );
AppendDataUnsafe( &fragment_size, sizeof( fragment_size ) );
AppendDataUnsafe( ptr + offset, fragment_size );
offset += fragment_size;
}
}
void Profiler::SendSourceLocation( uint64_t ptr )
{
auto srcloc = (const SourceLocationData*)ptr;

View File

@ -484,6 +484,27 @@ public:
TracyLfqCommit;
}
static tracy_force_inline void Blob( uint64_t encoding, const void* data, size_t size, int callstack )
{
#ifdef TRACY_ON_DEMAND
if( !GetProfiler().IsConnected() ) return;
#endif
if( callstack != 0 )
{
tracy::GetProfiler().SendCallstack( callstack );
}
auto ptr = tracy_malloc( size );
memcpy( ptr, data, size );
TracyQueuePrepare( callstack == 0 ? QueueType::Blob : QueueType::BlobCallstack );
MemWrite( &item->blobData.time, GetTime() );
MemWrite( &item->blobData.encoding, encoding );
MemWrite( &item->blobData.data, (uint64_t)ptr );
MemWrite( &item->blobData.size, (uint32_t)size );
TracyQueueCommit( blobDataThread );
}
static tracy_force_inline void MemAlloc( const void* ptr, size_t size, bool secure )
{
if( secure && !ProfilerAvailable() ) return;
@ -768,6 +789,8 @@ public:
void SendSecondString( const char* ptr ) { SendSecondString( ptr, strlen( ptr ) ); }
void SendSecondString( const char* ptr, size_t len );
void SendBlob( const char* ptr, size_t len );
// Allocated source location data layout:
// 2b payload size

View File

@ -16,6 +16,8 @@ enum class QueueType : uint8_t
MessageCallstack,
MessageColorCallstack,
MessageAppInfo,
Blob,
BlobCallstack,
ZoneBeginAllocSrcLoc,
ZoneBeginAllocSrcLocCallstack,
CallstackSerial,
@ -124,6 +126,7 @@ enum class QueueType : uint8_t
SymbolCode,
SourceCode,
FiberName,
BlobFragment,
NUM_TYPES
};
@ -395,6 +398,23 @@ struct QueueMessageColorFatThread : public QueueMessageColorFat
uint32_t thread;
};
struct QueueBlob
{
int64_t time;
};
struct QueueBlobData : public QueueBlob
{
uint32_t encoding;
uint64_t data; // ptr
uint32_t size;
};
struct QueueBlobDataThread : public QueueBlobData
{
uint32_t thread;
};
// Don't change order, only add new entries at the end, this is also used on trace dumps!
enum class GpuContextType : uint8_t
{
@ -740,6 +760,9 @@ struct QueueItem
QueueMessageFatThread messageFatThread;
QueueMessageColorFat messageColorFat;
QueueMessageColorFatThread messageColorFatThread;
QueueBlob blob;
QueueBlobData blobData;
QueueBlobDataThread blobDataThread;
QueueGpuNewContext gpuNewContext;
QueueGpuZoneBegin gpuZoneBegin;
QueueGpuZoneBeginLean gpuZoneBeginLean;
@ -797,6 +820,8 @@ static constexpr size_t QueueDataSize[] = {
sizeof( QueueHeader ) + sizeof( QueueMessage ), // callstack
sizeof( QueueHeader ) + sizeof( QueueMessageColor ), // callstack
sizeof( QueueHeader ) + sizeof( QueueMessage ), // app info
sizeof( QueueHeader ) + sizeof( QueueBlobData ),
sizeof( QueueHeader ) + sizeof( QueueBlobData ), // callstack
sizeof( QueueHeader ) + sizeof( QueueZoneBeginLean ), // allocated source location
sizeof( QueueHeader ) + sizeof( QueueZoneBeginLean ), // allocated source location, callstack
sizeof( QueueHeader ), // callstack memory
@ -907,6 +932,7 @@ static constexpr size_t QueueDataSize[] = {
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // symbol code
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // source code
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // fiber name
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // blob fragment
};
static_assert( QueueItemSize == 32, "Queue item size not 32 bytes" );

View File

@ -73,6 +73,8 @@
#define TracyMessageLC(x,y)
#define TracyAppInfo(x,y)
#define TracyBlob(x,y,z)
#define TracyAlloc(x,y)
#define TracyFree(x)
#define TracyMemoryDiscard(x)
@ -115,6 +117,8 @@
#define TracyMessageCS(x,y,z,w)
#define TracyMessageLCS(x,y,z)
#define TracyBlobS(x,y,z,w)
#define TracySourceCallbackRegister(x,y)
#define TracyParameterRegister(x,y)
#define TracyParameterSetup(x,y,z,w)
@ -203,6 +207,8 @@
# define TracyMessageC( txt, size, color ) tracy::Profiler::MessageColor( txt, size, color, TRACY_CALLSTACK )
# define TracyMessageLC( txt, color ) tracy::Profiler::MessageColor( txt, color, TRACY_CALLSTACK )
# define TracyBlob( encoding, data, size ) tracy::Profiler::Blob( encoding, data, size, TRACY_CALLSTACK )
# define TracyAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, false )
# define TracyFree( ptr ) tracy::Profiler::MemFreeCallstack( ptr, TRACY_CALLSTACK, false )
# define TracySecureAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, true )
@ -220,6 +226,8 @@
# define TracyMessageC( txt, size, color ) tracy::Profiler::MessageColor( txt, size, color, 0 )
# define TracyMessageLC( txt, color ) tracy::Profiler::MessageColor( txt, color, 0 )
# define TracyBlob( encoding, data, size ) tracy::Profiler::Blob( encoding, data, size, 0 )
# define TracyAlloc( ptr, size ) tracy::Profiler::MemAlloc( ptr, size, false )
# define TracyFree( ptr ) tracy::Profiler::MemFree( ptr, false )
# define TracySecureAlloc( ptr, size ) tracy::Profiler::MemAlloc( ptr, size, true )
@ -263,6 +271,8 @@
# define TracyMessageLS( txt, depth ) tracy::Profiler::Message( txt, depth )
# define TracyMessageCS( txt, size, color, depth ) tracy::Profiler::MessageColor( txt, size, color, depth )
# define TracyMessageLCS( txt, color, depth ) tracy::Profiler::MessageColor( txt, color, depth )
# define TracyBlobS( encoding, data, size, depth ) tracy::Profiler::Blob( encoding, data, size, depth )
#else
# define ZoneNamedS( varname, depth, active ) ZoneNamed( varname, active )
# define ZoneNamedNS( varname, name, depth, active ) ZoneNamedN( varname, name, active )
@ -293,6 +303,8 @@
# define TracyMessageLS( txt, depth ) TracyMessageL( txt )
# define TracyMessageCS( txt, size, color, depth ) TracyMessageC( txt, size, color )
# define TracyMessageLCS( txt, color, depth ) TracyMessageLC( txt, color )
# define TracyBlobS( encoding, data, size, depth ) TracyBlob( encoding, data, size )
#endif
#define TracySourceCallbackRegister( cb, data ) tracy::Profiler::SourceCallbackRegister( cb, data )

View File

@ -613,6 +613,14 @@ struct MessageData
enum { MessageDataSize = sizeof( MessageData ) };
struct BlobData
{
int64_t time;
int64_t encoding;
StringRef ref;
uint16_t thread;
Int24 callstack;
};
struct PlotItem
{
@ -673,6 +681,7 @@ struct ThreadData
Vector<short_ptr<ZoneEvent>> timeline;
Vector<short_ptr<ZoneEvent>> stack;
Vector<short_ptr<MessageData>> messages;
Vector<short_ptr<BlobData>> blobs;
uint32_t nextZoneId;
Vector<uint32_t> zoneIdStack;
#ifndef TRACY_NO_STATISTICS

View File

@ -275,6 +275,7 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit )
, m_callstackFrameStaging( nullptr )
, m_traceVersion( CurrentVersion )
, m_loadTime( 0 )
, m_blob( nullptr )
{
m_data.sourceLocationExpand.push_back( 0 );
m_data.localThreadCompress.InitZero();
@ -752,11 +753,11 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow
if( m_allowStringModification )
{
m_data.stringData.push_back( dst );
m_data.stringData.push_back( StringData(dst, ssz) );
}
else
{
m_data.stringData[i] = ( dst );
m_data.stringData[i] = StringData(dst, ssz );
}
pointerMap.emplace( ptr, dst );
@ -2382,7 +2383,7 @@ const char* Worker::GetString( const StringRef& ref ) const
if( ref.isidx )
{
assert( ref.active );
return m_data.stringData[ref.str];
return m_data.stringData[ref.str].c;
}
else
{
@ -2400,7 +2401,15 @@ const char* Worker::GetString( const StringRef& ref ) const
const char* Worker::GetString( const StringIdx& idx ) const
{
assert( idx.Active() );
return m_data.stringData[idx.Idx()];
return m_data.stringData[idx.Idx()].c;
}
const char* Worker::GetData( const StringRef& ref, size_t *sz ) const
{
assert ( ref.isidx );
assert( ref.active );
*sz = m_data.stringData[ref.str].sz;
return m_data.stringData[ref.str].c;
}
static const char* BadExternalThreadNames[] = {
@ -3055,6 +3064,9 @@ void Worker::DispatchFailure( const QueueItem& ev, const char*& ptr )
AddFiberName( ev.stringTransfer.ptr, ptr, sz );
m_serverQuerySpaceLeft++;
break;
case QueueType::BlobFragment:
m_serverQuerySpaceLeft++;
break;
case QueueType::PlotName:
case QueueType::FrameName:
case QueueType::ExternalName:
@ -3085,6 +3097,33 @@ void Worker::DispatchFailure( const QueueItem& ev, const char*& ptr )
AddSecondString( ptr, sz );
ptr += sz;
break;
case QueueType::BlobFragment:
{
ptr += sizeof( QueueHeader );
uint32_t full_size;
memcpy( &full_size, ptr, sizeof( uint32_t ) );
ptr += sizeof( uint32_t );
uint32_t offset;
memcpy( &offset, ptr, sizeof( uint32_t ) );
ptr += sizeof( uint32_t );
memcpy( &sz, ptr, sizeof( sz ) );
ptr += sizeof( sz );
if ( offset == 0 )
{
assert( m_blob == nullptr );
m_blob = (unsigned char *)malloc( full_size );
}
assert( m_blob != nullptr );
memcpy( m_blob, ptr, sz);
if (offset + sz > full_size)
{
AddSingleString( (const char*)m_blob, full_size );
free( m_blob );
m_blob = nullptr;
}
ptr += sz;
break;
}
default:
ptr += QueueDataSize[ev.hdr.idx];
switch( ev.hdr.type )
@ -3210,6 +3249,34 @@ bool Worker::DispatchProcess( const QueueItem& ev, const char*& ptr )
}
ptr += sz;
}
else if( ev.hdr.type == QueueType::BlobFragment )
{
uint32_t full_size;
memcpy( &full_size, ptr, sizeof( uint32_t ) );
ptr += sizeof( uint32_t );
uint32_t offset;
memcpy( &offset, ptr, sizeof( uint32_t ) );
ptr += sizeof( uint32_t );
uint32_t sz;
memcpy( &sz, ptr, sizeof( sz ) );
ptr += sizeof( sz );
if ( offset == 0 )
{
assert( m_blob == nullptr );
m_blob = (unsigned char *)malloc( full_size );
}
assert( m_blob != nullptr );
memcpy( m_blob, ptr, sz);
if (offset + sz >= full_size)
{
AddSingleString((const char*)m_blob, full_size );
free( m_blob );
m_blob = nullptr;
}
m_serverQuerySpaceLeft++;
ptr += sz;
}
else
{
uint16_t sz;
@ -3264,6 +3331,7 @@ bool Worker::DispatchProcess( const QueueItem& ev, const char*& ptr )
else
{
uint16_t sz;
uint32_t sz32;
switch( ev.hdr.type )
{
case QueueType::SingleStringData:
@ -3280,6 +3348,12 @@ bool Worker::DispatchProcess( const QueueItem& ev, const char*& ptr )
AddSecondString( ptr, sz );
ptr += sz;
return true;
case QueueType::BlobFragment:
ptr += sizeof( QueueHeader );
memcpy( &sz32, ptr, sizeof( sz32 ) );
ptr += sizeof( sz32 );
ptr += sz32;
return true;
default:
ptr += QueueDataSize[ev.hdr.idx];
return Process( ev );
@ -3384,6 +3458,39 @@ void Worker::InsertMessageData( MessageData* msg )
}
}
void Worker::InsertBlobData( BlobData* blob )
{
if( m_data.blobs.empty() )
{
m_data.blobs.push_back( blob );
}
else if( m_data.blobs.back()->time < blob->time )
{
m_data.blobs.push_back_non_empty( blob );
}
else
{
auto mit = std::lower_bound( m_data.blobs.begin(), m_data.blobs.end(), blob->time, [] ( const auto& lhs, const auto& rhs ) { return lhs->time < rhs; } );
m_data.blobs.insert( mit, blob );
}
auto td = GetCurrentThreadData();
auto vec = &td->blobs;
if( vec->empty() )
{
vec->push_back( blob );
}
else if( vec->back()->time < blob->time )
{
vec->push_back_non_empty( blob );
}
else
{
auto tmit = std::lower_bound( vec->begin(), vec->end(), blob->time, [] ( const auto& lhs, const auto& rhs ) { return lhs->time < rhs; } );
vec->insert( tmit, blob );
}
}
ThreadData* Worker::NoticeThreadReal( uint64_t thread )
{
auto it = m_threadMap.find( thread );
@ -4421,7 +4528,7 @@ StringLocation Worker::StoreString( const char* str, size_t sz )
ret.ptr = ptr;
ret.idx = m_data.stringData.size();
m_data.stringMap.emplace( charutil::StringKey { ptr, sz }, m_data.stringData.size() );
m_data.stringData.push_back( ptr );
m_data.stringData.push_back( { ptr, sz } );
}
else
{
@ -4556,6 +4663,12 @@ bool Worker::Process( const QueueItem& ev )
case QueueType::MessageAppInfo:
ProcessMessageAppInfo( ev.message );
break;
case QueueType::Blob:
ProcessBlob( ev.blobData );
break;
case QueueType::BlobCallstack:
ProcessBlobCallstack( ev.blobData );
break;
case QueueType::GpuNewContext:
ProcessGpuNewContext( ev.gpuNewContext );
break;
@ -5664,6 +5777,30 @@ void Worker::ProcessMessageAppInfo( const QueueMessage& ev )
if( m_data.lastTime < time ) m_data.lastTime = time;
}
void Worker::ProcessBlob( const QueueBlobData& ev )
{
auto td = GetCurrentThreadData();
auto blob = m_slab.Alloc<BlobData>();
const auto time = TscTime( ev.time );
blob->time = time;
blob->ref = StringRef( StringRef::Type::Idx, GetSingleStringIdx() );
blob->encoding = ev.encoding;
blob->thread = CompressThread( td->id );
blob->callstack.SetVal( 0 );
if( m_data.lastTime < time ) m_data.lastTime = time;
InsertBlobData( blob );
}
void Worker::ProcessBlobCallstack( const QueueBlobData& ev )
{
auto td = GetCurrentThreadData();
ProcessBlob( ev );
auto it = m_nextCallstack.find( td->id );
assert( it != m_nextCallstack.end() );
td->blobs.back()->callstack.SetVal( it->second );
it->second = 0;
}
void Worker::ProcessGpuNewContext( const QueueGpuNewContext& ev )
{
assert( !m_gpuCtxMap[ev.context] );
@ -7844,11 +7981,10 @@ void Worker::Write( FileWrite& f, bool fiDict )
f.Write( &sz, sizeof( sz ) );
for( auto& v : m_data.stringData )
{
uint64_t ptr = (uint64_t)v;
uint64_t ptr = (uint64_t)v.c;
f.Write( &ptr, sizeof( ptr ) );
sz = strlen( v );
f.Write( &sz, sizeof( sz ) );
f.Write( v, sz );
f.Write( &v.sz, sizeof( v.sz ) );
f.Write( v.c, sz );
}
sz = m_data.strings.size();

View File

@ -275,6 +275,12 @@ private:
bool isInline;
};
struct StringData
{
const char *c;
size_t sz;
};
struct DataBlock
{
std::mutex lock;
@ -282,6 +288,7 @@ private:
FrameData* framesBase;
Vector<GpuCtxData*> gpuData;
Vector<short_ptr<MessageData>> messages;
Vector<short_ptr<BlobData>> blobs;
StringDiscovery<PlotData*> plots;
Vector<ThreadData*> threads;
Vector<ZoneExtra> zoneExtra;
@ -299,7 +306,7 @@ private:
char cpuManufacturer[13];
unordered_flat_map<uint64_t, const char*> strings;
Vector<const char*> stringData;
Vector<StringData> stringData;
unordered_flat_map<charutil::StringKey, uint32_t, charutil::StringKey::Hasher, charutil::StringKey::Comparator> stringMap;
unordered_flat_map<uint64_t, const char*> threadNames;
unordered_flat_map<uint64_t, std::pair<const char*, const char*>> externalNames;
@ -535,6 +542,7 @@ public:
const unordered_flat_map<uint32_t, LockMap*>& GetLockMap() const { return m_data.lockMap; }
const Vector<short_ptr<MessageData>>& GetMessages() const { return m_data.messages; }
const Vector<short_ptr<BlobData>>& GetBlobs() const { return m_data.blobs; }
const Vector<GpuCtxData*>& GetGpuData() const { return m_data.gpuData; }
const Vector<PlotData*>& GetPlots() const { return m_data.plots.Data(); }
const Vector<ThreadData*>& GetThreadData() const { return m_data.threads; }
@ -583,6 +591,7 @@ public:
const char* GetString( const StringRef& ref ) const;
const char* GetString( const StringIdx& idx ) const;
const char* GetThreadName( uint64_t id ) const;
const char* GetData( const StringRef& ref, size_t *sz ) const;
bool IsThreadLocal( uint64_t id );
bool IsThreadFiber( uint64_t id );
const SourceLocation& GetSourceLocation( int16_t srcloc ) const;
@ -731,6 +740,8 @@ private:
tracy_force_inline void ProcessMessageColorCallstack( const QueueMessageColor& ev );
tracy_force_inline void ProcessMessageLiteralColorCallstack( const QueueMessageColorLiteral& ev );
tracy_force_inline void ProcessMessageAppInfo( const QueueMessage& ev );
tracy_force_inline void ProcessBlob( const QueueBlobData& ev );
tracy_force_inline void ProcessBlobCallstack( const QueueBlobData& ev );
tracy_force_inline void ProcessGpuNewContext( const QueueGpuNewContext& ev );
tracy_force_inline void ProcessGpuZoneBegin( const QueueGpuZoneBegin& ev, bool serial );
tracy_force_inline void ProcessGpuZoneBeginCallstack( const QueueGpuZoneBegin& ev, bool serial );
@ -823,6 +834,8 @@ private:
void InsertMessageData( MessageData* msg );
void InsertBlobData( BlobData* blob );
ThreadData* NoticeThreadReal( uint64_t thread );
ThreadData* NewThread( uint64_t thread, bool fiber, int32_t groupHint );
tracy_force_inline ThreadData* NoticeThread( uint64_t thread )
@ -1044,6 +1057,8 @@ private:
DataBlock m_data;
MbpsBlock m_mbpsData;
unsigned char *m_blob;
int m_traceVersion;
std::atomic<uint8_t> m_handshake { 0 };