mirror of
https://github.com/wolfpld/tracy.git
synced 2024-11-26 16:04:34 +00:00
Merge branch 'memory'
This commit is contained in:
commit
a319ce13e9
@ -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
|
||||
|
@ -144,7 +144,9 @@
|
||||
<ClInclude Include="..\..\..\common\TracyQueue.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySocket.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySystem.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_benaphore.h" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_sema.h" />
|
||||
<ClInclude Include="..\..\..\server\TracyCharUtil.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyEvent.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyFileWrite.hpp" />
|
||||
@ -153,9 +155,7 @@
|
||||
<ClInclude Include="..\..\..\server\TracySlab.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyVector.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyWorker.hpp" />
|
||||
<ClInclude Include="..\..\..\server\tracy_benaphore.h" />
|
||||
<ClInclude Include="..\..\..\server\tracy_flat_hash_map.hpp" />
|
||||
<ClInclude Include="..\..\..\server\tracy_sema.h" />
|
||||
<ClInclude Include="..\..\src\getopt.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
@ -59,15 +59,9 @@
|
||||
<ClInclude Include="..\..\..\common\TracySystem.hpp">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\tracy_benaphore.h">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\tracy_flat_hash_map.hpp">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\tracy_sema.h">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\TracyCharUtil.hpp">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
@ -98,5 +92,11 @@
|
||||
<ClInclude Include="..\..\..\common\TracyAlign.hpp">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\tracy_benaphore.h">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\tracy_sema.h">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -9,7 +9,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../../server/tracy_benaphore.h"
|
||||
#include "../../server/TracyFileWrite.hpp"
|
||||
#include "../../server/TracyMemory.hpp"
|
||||
#include "../../server/TracyWorker.hpp"
|
||||
|
88
client/TracyFastVector.hpp
Normal file
88
client/TracyFastVector.hpp
Normal file
@ -0,0 +1,88 @@
|
||||
#ifndef __TRACYFASTVECTOR_HPP__
|
||||
#define __TRACYFASTVECTOR_HPP__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "../common/TracyAlloc.hpp"
|
||||
#include "../common/TracyForceInline.hpp"
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
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
|
@ -16,6 +16,7 @@
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -108,7 +109,7 @@ struct ThreadNameData;
|
||||
std::atomic<ThreadNameData*> 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<NonRecursiveBenaphore> 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<uint8_t>( &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;
|
||||
|
@ -7,7 +7,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#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<QueueItem> m_serialQueue;
|
||||
NonRecursiveBenaphore m_serialLock;
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -30,9 +30,16 @@ namespace tracy
|
||||
// Semaphore (Windows)
|
||||
//---------------------------------------------------------
|
||||
|
||||
#include <windows.h>
|
||||
#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()
|
@ -137,6 +137,20 @@ struct GpuEvent
|
||||
enum { GpuEventSize = sizeof( GpuEvent ) };
|
||||
static_assert( std::is_standard_layout<GpuEvent>::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<MemEvent>::value, "MemEvent is not standard layout" );
|
||||
|
||||
#pragma pack()
|
||||
|
||||
|
||||
@ -208,6 +222,15 @@ struct PlotData
|
||||
uint64_t postponeTime;
|
||||
};
|
||||
|
||||
struct MemData
|
||||
{
|
||||
Vector<MemEvent> data;
|
||||
flat_hash_map<uint64_t, size_t, nohash<uint64_t>> active;
|
||||
uint64_t high = std::numeric_limits<uint64_t>::min();
|
||||
uint64_t low = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t usage = 0;
|
||||
};
|
||||
|
||||
struct StringLocation
|
||||
{
|
||||
const char* ptr;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 );
|
||||
|
@ -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<ZoneEvent*>& 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<class T>
|
||||
void View::ListMemData( T ptr, T end, std::function<const MemEvent*(T&)> 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<const MemEvent*> 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<decltype( match.begin() )>( 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<const MemEvent*> 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<decltype( items.begin() )>( 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<int8_t> View::GetMemoryPages() const
|
||||
{
|
||||
Vector<int8_t> 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<ZoneEvent*>* 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()
|
||||
{
|
||||
|
@ -2,14 +2,15 @@
|
||||
#define __TRACYVIEW_HPP__
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#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<class T>
|
||||
void ListMemData( T ptr, T end, std::function<const MemEvent*(T&)> 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<int8_t> GetMemoryPages() const;
|
||||
|
||||
flat_hash_map<const void*, bool, nohash<const void*>> m_visible;
|
||||
flat_hash_map<const void*, bool, nohash<const void*>> m_showFull;
|
||||
|
||||
@ -172,13 +180,13 @@ private:
|
||||
struct {
|
||||
enum : uint64_t { Unselected = std::numeric_limits<uint64_t>::max() - 1 };
|
||||
|
||||
bool show;
|
||||
bool show = false;
|
||||
std::vector<int32_t> match;
|
||||
std::map<uint64_t, Vector<ZoneEvent*>> 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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; i<sz; i++ )
|
||||
{
|
||||
f.Read( &mem->ptr, 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<ZoneEvent*>& 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<ZoneEvent*>& vec )
|
||||
|
@ -8,11 +8,11 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#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<MessageData*> messages;
|
||||
Vector<PlotData*> plots;
|
||||
Vector<ThreadData*> threads;
|
||||
MemData memory;
|
||||
uint64_t zonesCnt;
|
||||
int64_t lastTime;
|
||||
|
||||
@ -113,6 +114,7 @@ public:
|
||||
const Vector<GpuCtxData*>& GetGpuData() const { return m_data.gpuData; }
|
||||
const Vector<PlotData*>& GetPlots() const { return m_data.plots; }
|
||||
const Vector<ThreadData*>& 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 );
|
||||
|
@ -103,7 +103,9 @@
|
||||
<ClInclude Include="..\..\..\common\TracyQueue.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySocket.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySystem.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_benaphore.h" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_sema.h" />
|
||||
<ClInclude Include="..\..\..\imgui\imconfig.h" />
|
||||
<ClInclude Include="..\..\..\imgui\imgui.h" />
|
||||
<ClInclude Include="..\..\..\imgui\imgui_internal.h" />
|
||||
@ -124,10 +126,8 @@
|
||||
<ClInclude Include="..\..\..\server\TracyVector.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyView.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyWorker.hpp" />
|
||||
<ClInclude Include="..\..\..\server\tracy_benaphore.h" />
|
||||
<ClInclude Include="..\..\..\server\tracy_flat_hash_map.hpp" />
|
||||
<ClInclude Include="..\..\..\server\tracy_pdqsort.h" />
|
||||
<ClInclude Include="..\..\..\server\tracy_sema.h" />
|
||||
<ClInclude Include="..\..\libs\gl3w\GL\gl3w.h" />
|
||||
<ClInclude Include="..\..\libs\gl3w\GL\glcorearb.h" />
|
||||
<ClInclude Include="..\..\src\imgui_impl_glfw_gl3.h" />
|
||||
|
@ -146,12 +146,6 @@
|
||||
<ClInclude Include="..\..\..\server\TracyPopcnt.hpp">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\tracy_benaphore.h">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\tracy_sema.h">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\tracy_flat_hash_map.hpp">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
@ -167,6 +161,12 @@
|
||||
<ClInclude Include="..\..\..\common\TracyAlign.hpp">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\tracy_benaphore.h">
|
||||
<Filter>imgui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\tracy_sema.h">
|
||||
<Filter>imgui</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="DebugVis.natvis" />
|
||||
|
Loading…
Reference in New Issue
Block a user