diff --git a/Tracy.hpp b/Tracy.hpp index d8ed519b..bde4f7f1 100644 --- a/Tracy.hpp +++ b/Tracy.hpp @@ -29,6 +29,9 @@ #define TracyMessage(x,y) #define TracyMessageL(x) +#define TracyAlloc(x,y) +#define TracyFree(x) + #else #include "client/TracyLock.hpp" @@ -57,6 +60,9 @@ #define TracyMessage( txt, size ) tracy::Profiler::Message( txt, size ); #define TracyMessageL( txt ) tracy::Profiler::Message( txt ); +#define TracyAlloc( ptr, size ) tracy::Profiler::MemAlloc( ptr, size ); +#define TracyFree( ptr ) tracy::Profiler::MemFree( ptr ); + #endif #endif diff --git a/capture/build/win32/capture.vcxproj b/capture/build/win32/capture.vcxproj index 23761818..aef83464 100644 --- a/capture/build/win32/capture.vcxproj +++ b/capture/build/win32/capture.vcxproj @@ -144,7 +144,9 @@ + + @@ -153,9 +155,7 @@ - - diff --git a/capture/build/win32/capture.vcxproj.filters b/capture/build/win32/capture.vcxproj.filters index e9183070..01444809 100644 --- a/capture/build/win32/capture.vcxproj.filters +++ b/capture/build/win32/capture.vcxproj.filters @@ -59,15 +59,9 @@ common - - server - server - - server - server @@ -98,5 +92,11 @@ common + + common + + + common + \ No newline at end of file diff --git a/capture/src/capture.cpp b/capture/src/capture.cpp index 17e673ad..46dff6ca 100644 --- a/capture/src/capture.cpp +++ b/capture/src/capture.cpp @@ -9,7 +9,6 @@ #include #include -#include "../../server/tracy_benaphore.h" #include "../../server/TracyFileWrite.hpp" #include "../../server/TracyMemory.hpp" #include "../../server/TracyWorker.hpp" diff --git a/client/TracyFastVector.hpp b/client/TracyFastVector.hpp new file mode 100644 index 00000000..f0757dfe --- /dev/null +++ b/client/TracyFastVector.hpp @@ -0,0 +1,88 @@ +#ifndef __TRACYFASTVECTOR_HPP__ +#define __TRACYFASTVECTOR_HPP__ + +#include + +#include "../common/TracyAlloc.hpp" +#include "../common/TracyForceInline.hpp" + +namespace tracy +{ + +template +class FastVector +{ +public: + using iterator = T*; + using const_iterator = const T*; + + FastVector( size_t capacity ) + : m_ptr( (T*)tracy_malloc( sizeof( T ) * capacity ) ) + , m_size( 0 ) + , m_capacity( capacity ) + { + } + + FastVector( const FastVector& ) = delete; + FastVector( FastVector&& ) = delete; + + ~FastVector() + { + tracy_free( m_ptr ); + } + + FastVector& operator=( const FastVector& ) = delete; + FastVector& operator=( FastVector&& ) = delete; + + bool empty() const { return m_size == 0; } + size_t size() const { return m_size; } + + T* data() { return m_ptr; } + const T* data() const { return m_ptr; }; + + T* begin() { return m_ptr; } + const T* begin() const { return m_ptr; } + T* end() { return m_ptr + m_size; } + const T* end() const { return m_ptr + m_size; } + + T& front() { assert( m_size > 0 ); return m_ptr[0]; } + const T& front() const { assert( m_size > 0 ); return m_ptr[0]; } + + T& back() { assert( m_size > 0 ); return m_ptr[m_size - 1]; } + const T& back() const { assert( m_size > 0 ); return m_ptr[m_size - 1]; } + + T& operator[]( size_t idx ) { return m_ptr[idx]; } + const T& operator[]( size_t idx ) const { return m_ptr[idx]; } + + T* push_next() + { + T* ret; + if( m_size == m_capacity ) AllocMore(); + ret = m_ptr + m_size; + m_size++; + return ret; + } + + void clear() + { + m_size = 0; + } + +private: + tracy_no_inline void AllocMore() + { + m_capacity *= 2; + T* ptr = (T*)tracy_malloc( sizeof( T ) * m_capacity ); + memcpy( ptr, m_ptr, m_size * sizeof( T ) ); + tracy_free( m_ptr ); + m_ptr = ptr; + } + + T* m_ptr; + size_t m_size; + size_t m_capacity; +}; + +} + +#endif diff --git a/client/TracyProfiler.cpp b/client/TracyProfiler.cpp index 24953a16..173cdc74 100644 --- a/client/TracyProfiler.cpp +++ b/client/TracyProfiler.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -108,7 +109,7 @@ struct ThreadNameData; std::atomic init_order(104) s_threadNameData( nullptr ); #endif -static Profiler init_order(105) s_profiler; +Profiler init_order(105) s_profiler; enum { BulkSize = TargetFrameSize / QueueItemSize }; @@ -125,6 +126,7 @@ Profiler::Profiler() , m_bufferStart( 0 ) , m_itemBuf( (QueueItem*)tracy_malloc( sizeof( QueueItem ) * BulkSize ) ) , m_lz4Buf( (char*)tracy_malloc( LZ4Size + sizeof( lz4sz_t ) ) ) + , m_serialQueue( 1024*1024 ) { assert( !s_instance ); s_instance = this; @@ -212,11 +214,12 @@ void Profiler::Worker() for(;;) { const auto status = Dequeue( token ); - if( status == ConnectionLost ) + const auto serialStatus = DequeueSerial(); + if( status == ConnectionLost || serialStatus == ConnectionLost ) { break; } - else if( status == QueueEmpty ) + else if( status == QueueEmpty && serialStatus == QueueEmpty ) { if( ShouldExit() ) break; if( m_bufferOffset != m_bufferStart ) CommitData(); @@ -234,11 +237,12 @@ void Profiler::Worker() for(;;) { const auto status = Dequeue( token ); - if( status == ConnectionLost ) + const auto serialStatus = DequeueSerial(); + if( status == ConnectionLost || serialStatus == ConnectionLost ) { break; } - else if( status == QueueEmpty ) + else if( status == QueueEmpty && serialStatus == QueueEmpty ) { if( m_bufferOffset != m_bufferStart ) CommitData(); break; @@ -266,6 +270,7 @@ void Profiler::Worker() } } while( Dequeue( token ) == Success ) {} + while( DequeueSerial() == Success ) {} if( m_bufferOffset != m_bufferStart ) { if( !CommitData() ) return; @@ -325,6 +330,29 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token ) return Success; } +Profiler::DequeueStatus Profiler::DequeueSerial() +{ + std::lock_guard lock( m_serialLock ); + const auto sz = m_serialQueue.size(); + if( sz > 0 ) + { + auto item = m_serialQueue.data(); + auto end = item + sz; + while( item != end ) + { + const auto idx = MemRead( &item->hdr.idx ); + if( !AppendData( item, QueueDataSize[idx] ) ) return ConnectionLost; + item++; + } + m_serialQueue.clear(); + } + else + { + return QueueEmpty; + } + return Success; +} + bool Profiler::AppendData( const void* data, size_t len ) { auto ret = true; diff --git a/client/TracyProfiler.hpp b/client/TracyProfiler.hpp index 1666d0c9..9d254eac 100644 --- a/client/TracyProfiler.hpp +++ b/client/TracyProfiler.hpp @@ -7,7 +7,9 @@ #include #include "concurrentqueue.h" +#include "TracyFastVector.hpp" #include "../common/tracy_lz4.hpp" +#include "../common/tracy_benaphore.h" #include "../common/TracyQueue.hpp" #include "../common/TracyAlign.hpp" #include "../common/TracyAlloc.hpp" @@ -50,6 +52,9 @@ struct GpuCtxWrapper using Magic = moodycamel::ConcurrentQueueDefaultTraits::index_t; +class Profiler; +extern Profiler s_profiler; + class Profiler { public: @@ -186,6 +191,33 @@ public: tail.store( magic + 1, std::memory_order_release ); } + static tracy_force_inline void MemAlloc( const void* ptr, size_t size ) + { + const auto thread = GetThreadHandle(); + + s_profiler.m_serialLock.lock(); + auto item = s_profiler.m_serialQueue.push_next(); + MemWrite( &item->hdr.type, QueueType::MemAlloc ); + MemWrite( &item->memAlloc.time, GetTime() ); + MemWrite( &item->memAlloc.thread, thread ); + MemWrite( &item->memAlloc.ptr, (uint64_t)ptr ); + memcpy( &item->memAlloc.size, &size, 6 ); + s_profiler.m_serialLock.unlock(); + } + + static tracy_force_inline void MemFree( const void* ptr ) + { + const auto thread = GetThreadHandle(); + + s_profiler.m_serialLock.lock(); + auto item = s_profiler.m_serialQueue.push_next(); + MemWrite( &item->hdr.type, QueueType::MemFree ); + MemWrite( &item->memFree.time, GetTime() ); + MemWrite( &item->memFree.thread, thread ); + MemWrite( &item->memFree.ptr, (uint64_t)ptr ); + s_profiler.m_serialLock.unlock(); + } + static bool ShouldExit(); private: @@ -195,6 +227,7 @@ private: void Worker(); DequeueStatus Dequeue( moodycamel::ConsumerToken& token ); + DequeueStatus DequeueSerial(); bool AppendData( const void* data, size_t len ); bool CommitData(); bool NeedDataSize( size_t len ); @@ -225,6 +258,9 @@ private: QueueItem* m_itemBuf; char* m_lz4Buf; + + FastVector m_serialQueue; + NonRecursiveBenaphore m_serialLock; }; }; diff --git a/common/TracyQueue.hpp b/common/TracyQueue.hpp index 0762f39c..69c0f6b6 100644 --- a/common/TracyQueue.hpp +++ b/common/TracyQueue.hpp @@ -31,6 +31,8 @@ enum class QueueType : uint8_t GpuZoneEnd, GpuTime, GpuResync, + MemAlloc, + MemFree, StringData, ThreadName, CustomStringData, @@ -187,6 +189,21 @@ struct QueueGpuResync uint16_t context; }; +struct QueueMemAlloc +{ + int64_t time; + uint64_t thread; + uint64_t ptr; + char size[6]; +}; + +struct QueueMemFree +{ + int64_t time; + uint64_t thread; + uint64_t ptr; +}; + struct QueueHeader { union @@ -219,6 +236,8 @@ struct QueueItem QueueGpuZoneEnd gpuZoneEnd; QueueGpuTime gpuTime; QueueGpuResync gpuResync; + QueueMemAlloc memAlloc; + QueueMemFree memFree; }; }; @@ -251,6 +270,8 @@ static const size_t QueueDataSize[] = { sizeof( QueueHeader ) + sizeof( QueueGpuZoneEnd ), sizeof( QueueHeader ) + sizeof( QueueGpuTime ), sizeof( QueueHeader ) + sizeof( QueueGpuResync ), + sizeof( QueueHeader ) + sizeof( QueueMemAlloc ), + sizeof( QueueHeader ) + sizeof( QueueMemFree ), // keep all QueueStringTransfer below sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // string data sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // thread name diff --git a/server/tracy_benaphore.h b/common/tracy_benaphore.h similarity index 100% rename from server/tracy_benaphore.h rename to common/tracy_benaphore.h diff --git a/server/tracy_sema.h b/common/tracy_sema.h similarity index 91% rename from server/tracy_sema.h rename to common/tracy_sema.h index da3f088a..0b76e10c 100644 --- a/server/tracy_sema.h +++ b/common/tracy_sema.h @@ -30,9 +30,16 @@ namespace tracy // Semaphore (Windows) //--------------------------------------------------------- -#include -#undef min -#undef max +#ifndef MAXLONG +enum { MAXLONG = 0x7fffffff }; +enum { INFINITE = 0xFFFFFFFF }; +typedef void* HANDLE; + +extern "C" __declspec(dllimport) HANDLE __stdcall CreateSemaphoreA( void*, long, long, const char* ); +extern "C" __declspec(dllimport) int __stdcall CloseHandle( HANDLE ); +extern "C" __declspec(dllimport) unsigned long __stdcall WaitForSingleObject( HANDLE, unsigned long ); +extern "C" __declspec(dllimport) int __stdcall ReleaseSemaphore( HANDLE, long, long* ); +#endif class Semaphore { @@ -46,7 +53,7 @@ public: Semaphore(int initialCount = 0) { assert(initialCount >= 0); - m_hSema = CreateSemaphore(NULL, initialCount, MAXLONG, NULL); + m_hSema = CreateSemaphoreA(NULL, initialCount, MAXLONG, NULL); } ~Semaphore() diff --git a/server/TracyEvent.hpp b/server/TracyEvent.hpp index 463bd1ff..7819f846 100644 --- a/server/TracyEvent.hpp +++ b/server/TracyEvent.hpp @@ -137,6 +137,20 @@ struct GpuEvent enum { GpuEventSize = sizeof( GpuEvent ) }; static_assert( std::is_standard_layout::value, "GpuEvent is not standard layout" ); + +struct MemEvent +{ + uint64_t ptr; + uint64_t size; + int64_t timeAlloc; + uint16_t threadAlloc; + int64_t timeFree; + uint16_t threadFree; +}; + +enum { MemEventSize = sizeof( MemEvent ) }; +static_assert( std::is_standard_layout::value, "MemEvent is not standard layout" ); + #pragma pack() @@ -208,6 +222,15 @@ struct PlotData uint64_t postponeTime; }; +struct MemData +{ + Vector data; + flat_hash_map> active; + uint64_t high = std::numeric_limits::min(); + uint64_t low = std::numeric_limits::max(); + uint64_t usage = 0; +}; + struct StringLocation { const char* ptr; diff --git a/server/TracyFileRead.hpp b/server/TracyFileRead.hpp index 52c02849..a53342b5 100644 --- a/server/TracyFileRead.hpp +++ b/server/TracyFileRead.hpp @@ -38,12 +38,24 @@ public: } } + bool IsEOF() + { + if( m_lastBlock != BufSize && m_offset == m_lastBlock ) return true; + if( m_offset == BufSize ) + { + if( fseek( m_file, 1, SEEK_CUR ) != 0 ) return true; + fseek( m_file, -1, SEEK_CUR ); + } + return false; + } + private: FileRead( FILE* f ) : m_stream( LZ4_createStreamDecode() ) , m_file( f ) , m_offset( BufSize ) , m_active( 1 ) + , m_lastBlock( 0 ) {} tracy_force_inline void ReadSmall( void* ptr, size_t size ) @@ -65,7 +77,7 @@ private: uint32_t sz; fread( &sz, 1, sizeof( sz ), m_file ); fread( m_lz4buf, 1, sz, m_file ); - LZ4_decompress_safe_continue( m_stream, m_lz4buf, m_buf[m_active], sz, BufSize ); + m_lastBlock = LZ4_decompress_safe_continue( m_stream, m_lz4buf, m_buf[m_active], sz, BufSize ); } const auto sz = std::min( size, BufSize - m_offset ); @@ -84,6 +96,7 @@ private: char m_buf[2][BufSize]; size_t m_offset; uint8_t m_active; + int m_lastBlock; }; } diff --git a/server/TracyVector.hpp b/server/TracyVector.hpp index db2474f8..c96c251d 100644 --- a/server/TracyVector.hpp +++ b/server/TracyVector.hpp @@ -94,6 +94,12 @@ public: m_ptr[m_size++] = std::move( v ); } + T& push_next() + { + if( m_size == Capacity() ) AllocMore(); + return m_ptr[m_size++]; + } + T* insert( T* it, const T& v ) { assert( it >= m_ptr && it <= m_ptr + m_size ); diff --git a/server/TracyView.cpp b/server/TracyView.cpp index 730daaf0..643e8043 100644 --- a/server/TracyView.cpp +++ b/server/TracyView.cpp @@ -307,6 +307,8 @@ void View::DrawImpl() ImGui::SameLine(); if( ImGui::Button( "Statistics", ImVec2( bw, 0 ) ) ) m_showStatistics = true; ImGui::SameLine(); + if( ImGui::Button( "Memory", ImVec2( bw, 0 ) ) ) m_memInfo.show = true; + ImGui::SameLine(); ImGui::Text( "Frames: %-7" PRIu64 " Time span: %-10s View span: %-10s Zones: %-13s Queue delay: %s Timer resolution: %s", m_worker.GetFrameCount(), TimeToString( m_worker.GetLastTime() - m_worker.GetFrameBegin( 0 ) ), TimeToString( m_zvEnd - m_zvStart ), RealToString( m_worker.GetZoneCount(), true ), TimeToString( m_worker.GetDelay() ), TimeToString( m_worker.GetResolution() ) ); DrawFrames(); DrawZones(); @@ -321,6 +323,7 @@ void View::DrawImpl() if( m_showMessages ) DrawMessages(); if( m_findZone.show ) DrawFindZone(); if( m_showStatistics ) DrawStatistics(); + if( m_memInfo.show ) DrawMemory(); if( m_zoomAnim.active ) { @@ -1089,6 +1092,13 @@ void View::DrawZones() auto& io = ImGui::GetIO(); draw->AddLine( ImVec2( io.MousePos.x, linepos.y ), ImVec2( io.MousePos.x, linepos.y + lineh ), 0x33FFFFFF ); } + + if( m_memInfo.show && m_memInfo.restrictTime ) + { + const auto zvMid = ( m_zvEnd - m_zvStart ) / 2; + auto& io = ImGui::GetIO(); + draw->AddLine( ImVec2( wpos.x + zvMid * pxns, linepos.y ), ImVec2( wpos.x + zvMid * pxns, linepos.y + lineh ), 0x88FF44FF ); + } } int View::DrawZoneLevel( const Vector& vec, bool hover, double pxns, const ImVec2& wpos, int _offset, int depth ) @@ -2508,7 +2518,7 @@ void View::DrawZoneInfoWindow() cti[i] = uint32_t( i ); } - std::sort( cti.get(), cti.get() + ev.child.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } ); + pdqsort_branchless( cti.get(), cti.get() + ev.child.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } ); if( !ev.child.empty() ) { @@ -2667,7 +2677,7 @@ void View::DrawGpuInfoWindow() cti[i] = uint32_t( i ); } - std::sort( cti.get(), cti.get() + ev.child.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } ); + pdqsort_branchless( cti.get(), cti.get() + ev.child.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } ); if( !ev.child.empty() ) { @@ -3481,7 +3491,7 @@ void View::DrawFindZone() } if( m_findZone.sortByCounts ) { - std::sort( threads.begin(), threads.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second.size() > rhs->second.size(); } ); + pdqsort_branchless( threads.begin(), threads.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second.size() > rhs->second.size(); } ); } ImGui::BeginChild( "##zonesScroll", ImVec2( ImGui::GetWindowContentRegionWidth(), std::max( 200.f, ImGui::GetContentRegionAvail().y ) ) ); @@ -3647,6 +3657,481 @@ void View::DrawStatistics() ImGui::End(); } +template +void View::ListMemData( T ptr, T end, std::function DrawAddress ) +{ + ImGui::BeginChild( "##memScroll", ImVec2( 0, std::max( 200.f, ImGui::GetContentRegionAvail().y ) ) ); + ImGui::Columns( 7 ); + ImGui::Text( "Address" ); + ImGui::NextColumn(); + ImGui::Text( "Size" ); + ImGui::NextColumn(); + ImGui::Text( "Appeared at" ); + ImGui::NextColumn(); + ImGui::Text( "Duration" ); + ImGui::SameLine(); + ImGui::TextDisabled( "(?)" ); + if( ImGui::IsItemHovered() ) + { + ImGui::BeginTooltip(); + ImGui::Text( "Active allocations are displayed using green color." ); + ImGui::EndTooltip(); + } + + ImGui::NextColumn(); + ImGui::Text( "Thread" ); + ImGui::SameLine(); + ImGui::TextDisabled( "(?)" ); + if( ImGui::IsItemHovered() ) + { + ImGui::BeginTooltip(); + ImGui::Text( "Shows one thread if alloc and free was performed on the same thread." ); + ImGui::Text( "Otherwise two threads are displayed in order: alloc, free." ); + ImGui::EndTooltip(); + } + ImGui::NextColumn(); + ImGui::Text( "Zone alloc" ); + ImGui::NextColumn(); + ImGui::Text( "Zone free" ); + ImGui::SameLine(); + ImGui::TextDisabled( "(?)" ); + if( ImGui::IsItemHovered() ) + { + ImGui::BeginTooltip(); + ImGui::Text( "If alloc and free is performed in the same zone, it is displayed in yellow color." ); + ImGui::EndTooltip(); + } + ImGui::NextColumn(); + ImGui::Separator(); + int idx = 0; + while( ptr != end ) + { + auto v = DrawAddress( ptr ); + ImGui::NextColumn(); + ImGui::Text( "%s", RealToString( v->size, true ) ); + ImGui::NextColumn(); + ImGui::Text( "%s", TimeToString( v->timeAlloc - m_worker.GetFrameBegin( 0 ) ) ); + ImGui::NextColumn(); + if( v->timeFree < 0 ) + { + ImGui::TextColored( ImVec4( 0.6f, 1.f, 0.6f, 1.f ), "%s", TimeToString( m_worker.GetLastTime() - v->timeAlloc ) ); + ImGui::NextColumn(); + ImGui::Text( "%s", m_worker.GetThreadString( m_worker.DecompressThread( v->threadAlloc ) ) ); + } + else + { + ImGui::Text( "%s", TimeToString( v->timeFree - v->timeAlloc ) ); + ImGui::NextColumn(); + ImGui::Text( "%s", m_worker.GetThreadString( m_worker.DecompressThread( v->threadAlloc ) ) ); + if( v->threadAlloc != v->threadFree ) + { + ImGui::Text( "%s", m_worker.GetThreadString( m_worker.DecompressThread( v->threadFree ) ) ); + } + } + ImGui::NextColumn(); + auto zone = FindZoneAtTime( m_worker.DecompressThread( v->threadAlloc ), v->timeAlloc ); + if( !zone ) + { + ImGui::Text( "-" ); + } + else + { + const auto& srcloc = m_worker.GetSourceLocation( zone->srcloc ); + const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); + ImGui::PushID( idx++ ); + auto sel = ImGui::Selectable( txt, m_zoneInfoWindow == zone ); + auto hover = ImGui::IsItemHovered(); + ImGui::PopID(); + if( sel ) + { + m_zoneInfoWindow = zone; + } + if( hover ) + { + m_zoneHighlight = zone; + if( ImGui::IsMouseClicked( 2 ) ) + { + ZoomToZone( *zone ); + } + ZoneTooltip( *zone ); + } + } + ImGui::NextColumn(); + if( v->timeFree < 0 ) + { + ImGui::TextColored( ImVec4( 0.6f, 1.f, 0.6f, 1.f ), "active" ); + } + else + { + auto zoneFree = FindZoneAtTime( m_worker.DecompressThread( v->threadFree ), v->timeFree ); + if( !zoneFree ) + { + ImGui::Text( "-" ); + } + else + { + const auto& srcloc = m_worker.GetSourceLocation( zoneFree->srcloc ); + const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); + ImGui::PushID( idx++ ); + bool sel; + if( zoneFree == zone ) + { + sel = ImGui::Selectable( "", m_zoneInfoWindow == zoneFree ); + ImGui::SameLine(); + ImGui::TextColored( ImVec4( 1.f, 1.f, 0.6f, 1.f ), txt ); + } + else + { + sel = ImGui::Selectable( txt, m_zoneInfoWindow == zoneFree ); + } + auto hover = ImGui::IsItemHovered(); + ImGui::PopID(); + if( sel ) + { + m_zoneInfoWindow = zoneFree; + } + if( hover ) + { + m_zoneHighlight = zoneFree; + if( ImGui::IsMouseClicked( 2 ) ) + { + ZoomToZone( *zoneFree ); + } + ZoneTooltip( *zoneFree ); + } + } + } + ImGui::NextColumn(); + ptr++; + } + ImGui::EndColumns(); + ImGui::EndChild(); +} + +enum { ChunkBits = 10 }; +enum { PageBits = 10 }; +enum { PageSize = 1 << PageBits }; +enum { PageChunkBits = ChunkBits + PageBits }; +enum { PageChunkSize = 1 << PageChunkBits }; + +uint32_t MemDecayColor[256] = { + 0x0, 0xFF077F07, 0xFF078007, 0xFF078207, 0xFF078307, 0xFF078507, 0xFF078707, 0xFF078807, + 0xFF078A07, 0xFF078B07, 0xFF078D07, 0xFF078F07, 0xFF079007, 0xFF089208, 0xFF089308, 0xFF089508, + 0xFF089708, 0xFF089808, 0xFF089A08, 0xFF089B08, 0xFF089D08, 0xFF089F08, 0xFF08A008, 0xFF08A208, + 0xFF09A309, 0xFF09A509, 0xFF09A709, 0xFF09A809, 0xFF09AA09, 0xFF09AB09, 0xFF09AD09, 0xFF09AF09, + 0xFF09B009, 0xFF09B209, 0xFF09B309, 0xFF09B509, 0xFF0AB70A, 0xFF0AB80A, 0xFF0ABA0A, 0xFF0ABB0A, + 0xFF0ABD0A, 0xFF0ABF0A, 0xFF0AC00A, 0xFF0AC20A, 0xFF0AC30A, 0xFF0AC50A, 0xFF0AC70A, 0xFF0BC80B, + 0xFF0BCA0B, 0xFF0BCB0B, 0xFF0BCD0B, 0xFF0BCF0B, 0xFF0BD00B, 0xFF0BD20B, 0xFF0BD30B, 0xFF0BD50B, + 0xFF0BD70B, 0xFF0BD80B, 0xFF0BDA0B, 0xFF0CDB0C, 0xFF0CDD0C, 0xFF0CDF0C, 0xFF0CE00C, 0xFF0CE20C, + 0xFF0CE30C, 0xFF0CE50C, 0xFF0CE70C, 0xFF0CE80C, 0xFF0CEA0C, 0xFF0CEB0C, 0xFF0DED0D, 0xFF0DEF0D, + 0xFF0DF00D, 0xFF0DF20D, 0xFF0DF30D, 0xFF0DF50D, 0xFF0DF70D, 0xFF0DF80D, 0xFF0DFA0D, 0xFF0DFB0D, + 0xFF0DFD0D, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, + 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, + 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, + 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, + 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, + 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF12FF12, + 0x0, 0xFF1212FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, + 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, + 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, + 0xFF1010FF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, + 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, + 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, + 0xFF0D0DFD, 0xFF0D0DFB, 0xFF0D0DFA, 0xFF0D0DF8, 0xFF0D0DF7, 0xFF0D0DF5, 0xFF0D0DF3, 0xFF0D0DF2, + 0xFF0D0DF0, 0xFF0D0DEF, 0xFF0D0DED, 0xFF0C0CEB, 0xFF0C0CEA, 0xFF0C0CE8, 0xFF0C0CE7, 0xFF0C0CE5, + 0xFF0C0CE3, 0xFF0C0CE2, 0xFF0C0CE0, 0xFF0C0CDF, 0xFF0C0CDD, 0xFF0C0CDB, 0xFF0B0BDA, 0xFF0B0BD8, + 0xFF0B0BD7, 0xFF0B0BD5, 0xFF0B0BD3, 0xFF0B0BD2, 0xFF0B0BD0, 0xFF0B0BCF, 0xFF0B0BCD, 0xFF0B0BCB, + 0xFF0B0BCA, 0xFF0B0BC8, 0xFF0A0AC7, 0xFF0A0AC5, 0xFF0A0AC3, 0xFF0A0AC2, 0xFF0A0AC0, 0xFF0A0ABF, + 0xFF0A0ABD, 0xFF0A0ABB, 0xFF0A0ABA, 0xFF0A0AB8, 0xFF0A0AB7, 0xFF0909B5, 0xFF0909B3, 0xFF0909B2, + 0xFF0909B0, 0xFF0909AF, 0xFF0909AD, 0xFF0909AB, 0xFF0909AA, 0xFF0909A8, 0xFF0909A7, 0xFF0909A5, + 0xFF0909A3, 0xFF0808A2, 0xFF0808A0, 0xFF08089F, 0xFF08089D, 0xFF08089B, 0xFF08089A, 0xFF080898, + 0xFF080897, 0xFF080895, 0xFF080893, 0xFF080892, 0xFF070790, 0xFF07078F, 0xFF07078D, 0xFF07078B, + 0xFF07078A, 0xFF070788, 0xFF070787, 0xFF070785, 0xFF070783, 0xFF070782, 0xFF070780, 0xFF07077F, +}; + +void View::DrawMemory() +{ + auto& mem = m_worker.GetMemData(); + + ImGui::Begin( "Memory", &m_memInfo.show ); + + ImGui::Text( "Total allocations: %-15s Active allocations: %-15s Memory usage: %-15s Memory span: %s", + RealToString( mem.data.size(), true ), + RealToString( mem.active.size(), true ), + RealToString( mem.usage, true ), + RealToString( mem.high - mem.low, true ) ); + + ImGui::InputText( "", m_memInfo.pattern, 1024 ); + ImGui::SameLine(); + + if( ImGui::Button( "Find" ) ) + { + m_memInfo.ptrFind = strtoull( m_memInfo.pattern, nullptr, 0 ); + } + ImGui::SameLine(); + if( ImGui::Button( "Clear" ) ) + { + m_memInfo.ptrFind = 0; + m_memInfo.pattern[0] = '\0'; + } + ImGui::SameLine(); + ImGui::Checkbox( "Restrict time", &m_memInfo.restrictTime ); + ImGui::SameLine(); + ImGui::TextDisabled( "(?)" ); + if( ImGui::IsItemHovered() ) + { + ImGui::BeginTooltip(); + ImGui::Text( "Don't show allocations beyond the middle of timeline display." ); + ImGui::EndTooltip(); + } + + const auto zvMid = m_zvStart + ( m_zvEnd - m_zvStart ) / 2; + + ImGui::Separator(); + if( ImGui::TreeNodeEx( "Allocations", ImGuiTreeNodeFlags_DefaultOpen ) ) + { + if( m_memInfo.ptrFind != 0 ) + { + std::vector match; + match.reserve( mem.active.size() ); // heuristic + if( m_memInfo.restrictTime ) + { + for( auto& v : mem.data ) + { + if( v.ptr <= m_memInfo.ptrFind && v.ptr + v.size > m_memInfo.ptrFind && v.timeAlloc < zvMid ) + { + match.emplace_back( &v ); + } + } + } + else + { + for( auto& v : mem.data ) + { + if( v.ptr <= m_memInfo.ptrFind && v.ptr + v.size > m_memInfo.ptrFind ) + { + match.emplace_back( &v ); + } + } + } + + if( match.empty() ) + { + ImGui::Text( "Found no allocations at given address" ); + } + else + { + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( match.size(), true ) ); + ListMemData( match.begin(), match.end(), [this]( auto& it ) { + auto& v = *it; + if( v->ptr == m_memInfo.ptrFind ) + { + ImGui::Text( "0x%" PRIx64, m_memInfo.ptrFind ); + } + else + { + ImGui::Text( "0x%" PRIx64 "+%" PRIu64, v->ptr, m_memInfo.ptrFind - v->ptr ); + } + return v; + } ); + } + } + ImGui::TreePop(); + } + + ImGui::Separator(); + if( ImGui::TreeNode( "Active allocations" ) ) + { + uint64_t total = 0; + std::vector items; + items.reserve( mem.active.size() ); + if( m_memInfo.restrictTime ) + { + for( auto& v : mem.data ) + { + if( v.timeAlloc < zvMid && ( v.timeFree > zvMid || v.timeFree < 0 ) ) + { + items.emplace_back( &v ); + total += v.size; + } + } + } + else + { + auto ptr = mem.data.data(); + for( auto& v : mem.active ) + { + items.emplace_back( ptr + v.second ); + } + pdqsort_branchless( items.begin(), items.end(), []( const auto& lhs, const auto& rhs ) { return lhs->timeAlloc < rhs->timeAlloc; } ); + total = mem.usage; + } + + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( items.size(), true ) ); + ImGui::Text( "Memory usage: %s", RealToString( total, true ) ); + + ListMemData( items.begin(), items.end(), []( auto& v ) { + ImGui::Text( "0x%" PRIx64, (*v)->ptr ); + return *v; + } ); + ImGui::TreePop(); + } + + ImGui::Separator(); + if( ImGui::TreeNode( "Memory map" ) ) + { + ImGui::Text( "Single pixel: %s KB Single line: %s KB", RealToString( ( 1 << ChunkBits ) / 1024, true ), RealToString( PageChunkSize / 1024, true ) ); + + auto pages = GetMemoryPages(); + + const int8_t empty[PageSize] = {}; + const auto sz = pages.size() / PageSize; + auto pgptr = pages.data(); + const auto end = pgptr + sz * PageSize; + size_t lines = sz; + while( pgptr != end ) + { + if( memcmp( empty, pgptr, PageSize ) == 0 ) + { + pgptr += PageSize; + while( pgptr != end && memcmp( empty, pgptr, PageSize ) == 0 ) + { + lines--; + pgptr += PageSize; + } + } + else + { + pgptr += PageSize; + } + } + + ImGui::BeginChild( "##memMap", ImVec2( PageSize + 2, lines + 2 ), false ); + auto draw = ImGui::GetWindowDrawList(); + const auto wpos = ImGui::GetCursorScreenPos() + ImVec2( 1, 1 ); + draw->AddRect( wpos - ImVec2( 1, 1 ), wpos + ImVec2( PageSize + 1, lines + 1 ), 0xFF666666 ); + draw->AddRectFilled( wpos, wpos + ImVec2( PageSize, lines ), 0xFF444444 ); + + size_t line = 0; + pgptr = pages.data(); + while( pgptr != end ) + { + if( memcmp( empty, pgptr, PageSize ) == 0 ) + { + pgptr += PageSize; + draw->AddLine( wpos + ImVec2( 0, line ), wpos + ImVec2( PageSize, line ), 0x11000000 ); + line++; + while( pgptr != end && memcmp( empty, pgptr, PageSize ) == 0 ) pgptr += PageSize; + } + else + { + size_t idx = 0; + while( idx < PageSize ) + { + if( pgptr[idx] == 0 ) + { + do + { + idx++; + } + while( idx < PageSize && pgptr[idx] == 0 ); + } + else + { + auto val = pgptr[idx]; + const auto i0 = idx; + do + { + idx++; + } + while( idx < PageSize && pgptr[idx] == val ); + draw->AddLine( wpos + ImVec2( i0, line ), wpos + ImVec2( idx, line ), MemDecayColor[(uint8_t)val] ); + } + } + line++; + pgptr += PageSize; + } + } + + ImGui::EndChild(); + ImGui::TreePop(); + } + + ImGui::End(); +} + +Vector View::GetMemoryPages() const +{ + Vector ret; + + const auto& mem = m_worker.GetMemData(); + const auto span = mem.high - mem.low; + const auto pages = ( span / PageChunkSize ) + 1; + + ret.reserve_and_use( pages * PageSize ); + auto pgptr = ret.data(); + memset( pgptr, 0, pages * PageSize ); + + const auto memlow = mem.low; + + if( m_memInfo.restrictTime ) + { + const auto zvMid = m_zvStart + ( m_zvEnd - m_zvStart ) / 2; + for( auto& alloc : mem.data ) + { + if( m_memInfo.restrictTime && alloc.timeAlloc > zvMid ) break; + + const auto a0 = alloc.ptr - memlow; + const auto a1 = a0 + alloc.size; + int8_t val = alloc.timeFree < 0 ? + int8_t( std::max( int64_t( 1 ), 127 - ( ( zvMid - alloc.timeAlloc ) >> 24 ) ) ) : + ( alloc.timeFree > zvMid ? + int8_t( std::max( int64_t( 1 ), 127 - ( ( zvMid - alloc.timeAlloc ) >> 24 ) ) ) : + int8_t( -std::max( int64_t( 1 ), 127 - ( ( zvMid - alloc.timeFree ) >> 24 ) ) ) ); + + const auto c0 = a0 >> ChunkBits; + const auto c1 = a1 >> ChunkBits; + + if( c0 == c1 ) + { + pgptr[c0] = val; + } + else + { + memset( pgptr + c0, val, c1 - c0 + 1 ); + } + } + } + else + { + const auto lastTime = m_worker.GetLastTime(); + for( auto& alloc : mem.data ) + { + const auto a0 = alloc.ptr - memlow; + const auto a1 = a0 + alloc.size; + const int8_t val = alloc.timeFree < 0 ? + int8_t( std::max( int64_t( 1 ), 127 - ( ( lastTime - std::min( lastTime, alloc.timeAlloc ) ) >> 24 ) ) ) : + int8_t( -std::max( int64_t( 1 ), 127 - ( ( lastTime - std::min( lastTime, alloc.timeFree ) ) >> 24 ) ) ); + + const auto c0 = a0 >> ChunkBits; + const auto c1 = a1 >> ChunkBits; + + if( c0 == c1 ) + { + pgptr[c0] = val; + } + else + { + memset( pgptr + c0, val, c1 - c0 + 1 ); + } + } + } + + return ret; +} + uint32_t View::GetZoneColor( const ZoneEvent& ev ) { const auto& srcloc = m_worker.GetSourceLocation( ev.srcloc ); @@ -3928,6 +4413,34 @@ uint64_t View::GetZoneThread( const GpuEvent& zone ) const return 0; } +const ZoneEvent* View::FindZoneAtTime( uint64_t thread, int64_t time ) const +{ + // TODO add thread rev-map + ThreadData* td = nullptr; + for( const auto& t : m_worker.GetThreadData() ) + { + if( t->id == thread ) + { + td = t; + break; + } + } + if( !td ) return nullptr; + + const Vector* timeline = &td->timeline; + if( timeline->empty() ) return nullptr; + ZoneEvent* ret = nullptr; + for(;;) + { + auto it = std::upper_bound( timeline->begin(), timeline->end(), time, [] ( const auto& l, const auto& r ) { return l < r->start; } ); + if( it != timeline->begin() ) --it; + if( (*it)->start > time || ( (*it)->end >= 0 && (*it)->end < time ) ) return ret; + ret = *it; + if( (*it)->child.empty() ) return ret; + timeline = &(*it)->child; + } +} + #ifndef TRACY_NO_STATISTICS void View::FindZones() { diff --git a/server/TracyView.hpp b/server/TracyView.hpp index 6b930e32..fd093299 100644 --- a/server/TracyView.hpp +++ b/server/TracyView.hpp @@ -2,14 +2,15 @@ #define __TRACYVIEW_HPP__ #include +#include #include #include #include #include +#include "../common/tracy_benaphore.h" #include "TracyVector.hpp" #include "TracyWorker.hpp" -#include "tracy_benaphore.h" #include "tracy_flat_hash_map.hpp" struct ImVec2; @@ -74,6 +75,10 @@ private: void DrawMessages(); void DrawFindZone(); void DrawStatistics(); + void DrawMemory(); + + template + void ListMemData( T ptr, T end, std::function DrawAddress ); void DrawInfoWindow(); void DrawZoneInfoWindow(); @@ -98,11 +103,14 @@ private: const GpuEvent* GetZoneParent( const GpuEvent& zone ) const; uint64_t GetZoneThread( const ZoneEvent& zone ) const; uint64_t GetZoneThread( const GpuEvent& zone ) const; + const ZoneEvent* FindZoneAtTime( uint64_t thread, int64_t time ) const; #ifndef TRACY_NO_STATISTICS void FindZones(); #endif + Vector GetMemoryPages() const; + flat_hash_map> m_visible; flat_hash_map> m_showFull; @@ -172,13 +180,13 @@ private: struct { enum : uint64_t { Unselected = std::numeric_limits::max() - 1 }; - bool show; + bool show = false; std::vector match; std::map> threads; size_t processed; int selMatch = 0; uint64_t selThread = Unselected; - char pattern[1024] = { "" }; + char pattern[1024] = {}; bool logVal = false; bool logTime = false; bool cumulateTime = false; @@ -201,6 +209,13 @@ private: processed = 0; } } m_findZone; + + struct { + bool show = false; + char pattern[1024] = {}; + uint64_t ptrFind = 0; + bool restrictTime = false; + } m_memInfo; }; } diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 43463b1d..93ec9937 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -243,6 +243,35 @@ Worker::Worker( FileRead& f ) f.Read( pd->data.data(), psz * sizeof( PlotItem ) ); m_data.plots.push_back_no_space_check( pd ); } + + // Support pre-0.3 traces + if( f.IsEOF() ) return; + + f.Read( &sz, sizeof( sz ) ); + m_data.memory.data.reserve_and_use( sz ); + auto mem = m_data.memory.data.data(); + for( uint64_t i=0; iptr, sizeof( mem->ptr ) ); + f.Read( &mem->size, sizeof( mem->size ) ); + f.Read( &mem->timeAlloc, sizeof( mem->timeAlloc ) ); + f.Read( &mem->timeFree, sizeof( mem->timeFree ) ); + uint64_t t; + f.Read( &t, sizeof( t ) ); + mem->threadAlloc = CompressThread( t ); + f.Read( &t, sizeof( t ) ); + mem->threadFree = CompressThread( t ); + + if( mem->timeFree < 0 ) + { + m_data.memory.active.emplace( mem->ptr, i ); + } + + mem++; + } + f.Read( &m_data.memory.high, sizeof( m_data.memory.high ) ); + f.Read( &m_data.memory.low, sizeof( m_data.memory.low ) ); + f.Read( &m_data.memory.usage, sizeof( m_data.memory.usage ) ); } Worker::~Worker() @@ -1200,6 +1229,12 @@ void Worker::Process( const QueueItem& ev ) case QueueType::GpuResync: ProcessGpuResync( ev.gpuResync ); break; + case QueueType::MemAlloc: + ProcessMemAlloc( ev.memAlloc ); + break; + case QueueType::MemFree: + ProcessMemFree( ev.memFree ); + break; case QueueType::Terminate: m_terminate = true; break; @@ -1625,6 +1660,40 @@ void Worker::ProcessGpuResync( const QueueGpuResync& ev ) } } +void Worker::ProcessMemAlloc( const QueueMemAlloc& ev ) +{ + const auto time = TscTime( ev.time ); + + assert( m_data.memory.active.find( ev.ptr ) == m_data.memory.active.end() ); + assert( m_data.memory.data.empty() || m_data.memory.data.back().timeAlloc <= time ); + + m_data.memory.active.emplace( ev.ptr, m_data.memory.data.size() ); + + auto& mem = m_data.memory.data.push_next(); + mem.ptr = ev.ptr; + mem.size = 0; + memcpy( &mem.size, ev.size, 6 ); + mem.timeAlloc = time; + mem.threadAlloc = CompressThread( ev.thread ); + mem.timeFree = -1; + mem.threadFree = 0; + + m_data.memory.low = std::min( m_data.memory.low, mem.ptr ); + m_data.memory.high = std::max( m_data.memory.high, mem.ptr + mem.size ); + m_data.memory.usage += mem.size; +} + +void Worker::ProcessMemFree( const QueueMemFree& ev ) +{ + auto it = m_data.memory.active.find( ev.ptr ); + assert( it != m_data.memory.active.end() ); + auto& mem = m_data.memory.data[it->second]; + mem.timeFree = TscTime( ev.time ); + mem.threadFree = CompressThread( ev.thread ); + m_data.memory.usage -= mem.size; + m_data.memory.active.erase( it ); +} + void Worker::ReadTimeline( FileRead& f, Vector& vec, uint16_t thread ) { uint64_t sz; @@ -1838,6 +1907,23 @@ void Worker::Write( FileWrite& f ) f.Write( &sz, sizeof( sz ) ); f.Write( plot->data.data(), sizeof( PlotItem ) * sz ); } + + sz = m_data.memory.data.size(); + f.Write( &sz, sizeof( sz ) ); + for( auto& mem : m_data.memory.data ) + { + f.Write( &mem.ptr, sizeof( mem.ptr ) ); + f.Write( &mem.size, sizeof( mem.size ) ); + f.Write( &mem.timeAlloc, sizeof( mem.timeAlloc ) ); + f.Write( &mem.timeFree, sizeof( mem.timeFree ) ); + uint64_t t = DecompressThread( mem.threadAlloc ); + f.Write( &t, sizeof( t ) ); + t = DecompressThread( mem.threadFree ); + f.Write( &t, sizeof( t ) ); + } + f.Write( &m_data.memory.high, sizeof( m_data.memory.high ) ); + f.Write( &m_data.memory.low, sizeof( m_data.memory.low ) ); + f.Write( &m_data.memory.usage, sizeof( m_data.memory.usage ) ); } void Worker::WriteTimeline( FileWrite& f, const Vector& vec ) diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index 748181de..64b26a1e 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -8,11 +8,11 @@ #include #include +#include "../common/tracy_benaphore.h" #include "../common/tracy_lz4.hpp" #include "../common/TracyForceInline.hpp" #include "../common/TracyQueue.hpp" #include "../common/TracySocket.hpp" -#include "tracy_benaphore.h" #include "tracy_flat_hash_map.hpp" #include "TracyEvent.hpp" #include "TracySlab.hpp" @@ -57,6 +57,7 @@ class Worker Vector messages; Vector plots; Vector threads; + MemData memory; uint64_t zonesCnt; int64_t lastTime; @@ -113,6 +114,7 @@ public: const Vector& GetGpuData() const { return m_data.gpuData; } const Vector& GetPlots() const { return m_data.plots; } const Vector& GetThreadData() const { return m_data.threads; } + const MemData& GetMemData() const { return m_data.memory; } // Some zones may have incomplete timing data (only start time is available, end hasn't arrived yet). // GetZoneEnd() will try to infer the end time by looking at child zones (parent zone can't end @@ -177,6 +179,8 @@ private: tracy_force_inline void ProcessGpuZoneEnd( const QueueGpuZoneEnd& ev ); tracy_force_inline void ProcessGpuTime( const QueueGpuTime& ev ); tracy_force_inline void ProcessGpuResync( const QueueGpuResync& ev ); + tracy_force_inline void ProcessMemAlloc( const QueueMemAlloc& ev ); + tracy_force_inline void ProcessMemFree( const QueueMemFree& ev ); tracy_force_inline void CheckSourceLocation( uint64_t ptr ); void NewSourceLocation( uint64_t ptr ); @@ -204,6 +208,7 @@ private: void InsertPlot( PlotData* plot, int64_t time, double val ); void HandlePlotName( uint64_t name, char* str, size_t sz ); + void HandlePostponedPlots(); StringLocation StoreString( char* str, size_t sz ); diff --git a/standalone/build/win32/Tracy.vcxproj b/standalone/build/win32/Tracy.vcxproj index 50e15091..9b5101fc 100644 --- a/standalone/build/win32/Tracy.vcxproj +++ b/standalone/build/win32/Tracy.vcxproj @@ -103,7 +103,9 @@ + + @@ -124,10 +126,8 @@ - - diff --git a/standalone/build/win32/Tracy.vcxproj.filters b/standalone/build/win32/Tracy.vcxproj.filters index 0fdd0304..fe6814c7 100644 --- a/standalone/build/win32/Tracy.vcxproj.filters +++ b/standalone/build/win32/Tracy.vcxproj.filters @@ -146,12 +146,6 @@ server - - server - - - server - server @@ -167,6 +161,12 @@ common + + imgui + + + imgui +