diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj
index 6f532edd..72050189 100644
--- a/profiler/build/win32/Tracy.vcxproj
+++ b/profiler/build/win32/Tracy.vcxproj
@@ -138,6 +138,7 @@
+
diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters
index 1452d027..be312937 100644
--- a/profiler/build/win32/Tracy.vcxproj.filters
+++ b/profiler/build/win32/Tracy.vcxproj.filters
@@ -252,6 +252,9 @@
server
+
+ server
+
diff --git a/server/TracyView.cpp b/server/TracyView.cpp
index 4ff91904..6adf2ddc 100644
--- a/server/TracyView.cpp
+++ b/server/TracyView.cpp
@@ -19,7 +19,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -63,16 +62,6 @@ namespace tracy
double s_time = 0;
-constexpr const char* GpuContextNames[] = {
- "Invalid",
- "OpenGL",
- "Vulkan",
- "OpenCL",
- "Direct3D 12",
- "Direct3D 11"
-};
-
-
static inline uint64_t GetThreadBit( uint8_t thread )
{
return uint64_t( 1 ) << thread;
@@ -8739,705 +8728,6 @@ void View::DrawGpuInfoChildren( const V& children, int64_t ztime )
}
}
-void View::DrawOptions()
-{
- ImGui::Begin( "Options", &m_showOptions, ImGuiWindowFlags_AlwaysAutoResize );
- if( ImGui::GetCurrentWindowRead()->SkipItems ) { ImGui::End(); return; }
-
- const auto scale = GetScale();
- bool val = m_vd.drawEmptyLabels;
- ImGui::Checkbox( ICON_FA_EXPAND " Draw empty labels", &val );
- m_vd.drawEmptyLabels = val;
- val = m_vd.drawFrameTargets;
- ImGui::Checkbox( ICON_FA_FLAG_CHECKERED " Draw frame targets", &val );
- m_vd.drawFrameTargets = val;
- ImGui::Indent();
- int tmp = m_vd.frameTarget;
- ImGui::SetNextItemWidth( 90 * scale );
- if( ImGui::InputInt( "Target FPS", &tmp ) )
- {
- if( tmp < 1 ) tmp = 1;
- m_vd.frameTarget = tmp;
- }
- ImGui::SameLine();
- TextDisabledUnformatted( TimeToString( 1000*1000*1000 / tmp ) );
- ImGui::Unindent();
- if( m_worker.HasContextSwitches() )
- {
- ImGui::Separator();
- val = m_vd.drawContextSwitches;
- ImGui::Checkbox( ICON_FA_HIKING " Draw context switches", &val );
- m_vd.drawContextSwitches = val;
- ImGui::Indent();
- val = m_vd.darkenContextSwitches;
- SmallCheckbox( ICON_FA_MOON " Darken inactive threads", &val );
- m_vd.darkenContextSwitches = val;
- ImGui::Unindent();
- val = m_vd.drawCpuData;
- ImGui::Checkbox( ICON_FA_SLIDERS_H " Draw CPU data", &val );
- m_vd.drawCpuData = val;
- ImGui::Indent();
- val = m_vd.drawCpuUsageGraph;
- SmallCheckbox( ICON_FA_SIGNATURE " Draw CPU usage graph", &val );
- m_vd.drawCpuUsageGraph = val;
- ImGui::Unindent();
- }
-
- if( m_worker.GetCallstackSampleCount() != 0 )
- {
- val = m_vd.drawSamples;
- ImGui::Checkbox( ICON_FA_EYE_DROPPER " Draw stack samples", &val );
- m_vd.drawSamples = val;
- }
-
- const auto& gpuData = m_worker.GetGpuData();
- if( !gpuData.empty() )
- {
- ImGui::Separator();
- val = m_vd.drawGpuZones;
- ImGui::Checkbox( ICON_FA_EYE " Draw GPU zones", &val );
- m_vd.drawGpuZones = val;
- const auto expand = ImGui::TreeNode( "GPU zones" );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%zu)", gpuData.size() );
- if( expand )
- {
- for( size_t i=0; ithreadData.begin()->second.timeline;
- char buf[1024];
- sprintf( buf, "%s context %zu", GpuContextNames[(int)gpuData[i]->type], i );
- SmallCheckbox( buf, &Vis( gpuData[i] ).visible );
- ImGui::SameLine();
- if( gpuData[i]->threadData.size() == 1 )
- {
- ImGui::TextDisabled( "%s top level zones", RealToString( timeline.size() ) );
- }
- else
- {
- ImGui::TextDisabled( "%s threads", RealToString( gpuData[i]->threadData.size() ) );
- }
- if( gpuData[i]->name.Active() )
- {
- ImGui::PushFont( m_smallFont );
- TextFocused( "Name:", m_worker.GetString( gpuData[i]->name ) );
- ImGui::PopFont();
- }
- if( !gpuData[i]->hasCalibration )
- {
- ImGui::TreePush();
- auto& drift = GpuDrift( gpuData[i] );
- ImGui::SetNextItemWidth( 120 * scale );
- ImGui::PushID( i );
- ImGui::InputInt( "Drift (ns/s)", &drift );
- ImGui::PopID();
- if( timeline.size() > 1 )
- {
- ImGui::SameLine();
- if( ImGui::Button( ICON_FA_ROBOT " Auto" ) )
- {
- size_t lastidx = 0;
- if( timeline.is_magic() )
- {
- auto& tl = *((Vector*)&timeline);
- for( size_t j=tl.size()-1; j > 0; j-- )
- {
- if( tl[j].GpuEnd() >= 0 )
- {
- lastidx = j;
- break;
- }
- }
- }
- else
- {
- for( size_t j=timeline.size()-1; j > 0; j-- )
- {
- if( timeline[j]->GpuEnd() >= 0 )
- {
- lastidx = j;
- break;
- }
- }
- }
-
- enum { NumSlopes = 10000 };
- std::random_device rd;
- std::default_random_engine gen( rd() );
- std::uniform_int_distribution dist( 0, lastidx - 1 );
- float slopes[NumSlopes];
- size_t idx = 0;
- if( timeline.is_magic() )
- {
- auto& tl = *((Vector*)&timeline);
- do
- {
- const auto p0 = dist( gen );
- const auto p1 = dist( gen );
- if( p0 != p1 )
- {
- slopes[idx++] = float( 1.0 - double( tl[p1].GpuStart() - tl[p0].GpuStart() ) / double( tl[p1].CpuStart() - tl[p0].CpuStart() ) );
- }
- }
- while( idx < NumSlopes );
- }
- else
- {
- do
- {
- const auto p0 = dist( gen );
- const auto p1 = dist( gen );
- if( p0 != p1 )
- {
- slopes[idx++] = float( 1.0 - double( timeline[p1]->GpuStart() - timeline[p0]->GpuStart() ) / double( timeline[p1]->CpuStart() - timeline[p0]->CpuStart() ) );
- }
- }
- while( idx < NumSlopes );
- }
- std::sort( slopes, slopes+NumSlopes );
- drift = int( 1000000000 * -slopes[NumSlopes/2] );
- }
- }
- ImGui::TreePop();
- }
- }
- ImGui::TreePop();
- }
- }
-
- ImGui::Separator();
- val = m_vd.drawZones;
- ImGui::Checkbox( ICON_FA_MICROCHIP " Draw CPU zones", &val );
- ImGui::Indent();
- m_vd.drawZones = val;
-
-#ifndef TRACY_NO_STATISTICS
- if( m_worker.AreGhostZonesReady() && m_worker.GetGhostZonesCount() != 0 )
- {
- val = m_vd.ghostZones;
- SmallCheckbox( ICON_FA_GHOST " Draw ghost zones", &val );
- m_vd.ghostZones = val;
- }
-#endif
-
- int ival = m_vd.dynamicColors;
- ImGui::TextUnformatted( ICON_FA_PALETTE " Zone colors" );
- ImGui::SameLine();
- bool forceColors = m_vd.forceColors;
- if( SmallCheckbox( "Ignore custom", &forceColors ) ) m_vd.forceColors = forceColors;
- ImGui::Indent();
- ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
- ImGui::RadioButton( "Static", &ival, 0 );
- ImGui::RadioButton( "Thread dynamic", &ival, 1 );
- ImGui::RadioButton( "Source location dynamic", &ival, 2 );
- ImGui::PopStyleVar();
- ImGui::Unindent();
- m_vd.dynamicColors = ival;
- ival = (int)m_namespace;
- ImGui::TextUnformatted( ICON_FA_BOX_OPEN " Namespaces" );
- ImGui::Indent();
- ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
- ImGui::RadioButton( "Full", &ival, 0 );
- ImGui::RadioButton( "Shortened", &ival, 1 );
- ImGui::RadioButton( "None", &ival, 2 );
- ImGui::PopStyleVar();
- ImGui::Unindent();
- m_namespace = (Namespace)ival;
- ImGui::Unindent();
-
- if( !m_worker.GetLockMap().empty() )
- {
- size_t lockCnt = 0;
- size_t singleCnt = 0;
- size_t multiCntCont = 0;
- size_t multiCntUncont = 0;
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->valid && !l.second->timeline.empty() )
- {
- lockCnt++;
- if( l.second->threadList.size() == 1 )
- {
- singleCnt++;
- }
- else if( l.second->isContended )
- {
- multiCntCont++;
- }
- else
- {
- multiCntUncont++;
- }
- }
- }
-
- ImGui::Separator();
- val = m_vd.drawLocks;
- ImGui::Checkbox( ICON_FA_LOCK " Draw locks", &val );
- m_vd.drawLocks = val;
- ImGui::SameLine();
- val = m_vd.onlyContendedLocks;
- ImGui::Checkbox( "Only contended", &val );
- m_vd.onlyContendedLocks = val;
- const auto expand = ImGui::TreeNode( "Locks" );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%zu)", lockCnt );
- TooltipIfHovered( "Locks with no recorded events are counted, but not listed." );
- if( expand )
- {
- ImGui::SameLine();
- if( ImGui::SmallButton( "Select all" ) )
- {
- for( const auto& l : m_worker.GetLockMap() )
- {
- Vis( l.second ).visible = true;
- }
- }
- ImGui::SameLine();
- if( ImGui::SmallButton( "Unselect all" ) )
- {
- for( const auto& l : m_worker.GetLockMap() )
- {
- Vis( l.second ).visible = false;
- }
- }
- ImGui::SameLine();
- DrawHelpMarker( "Right click on lock name to open lock information window." );
-
- const bool multiExpand = ImGui::TreeNodeEx( "Contended locks present in multiple threads", ImGuiTreeNodeFlags_DefaultOpen );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%zu)", multiCntCont );
- if( multiExpand )
- {
- ImGui::SameLine();
- if( ImGui::SmallButton( "Select all" ) )
- {
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->threadList.size() != 1 && l.second->isContended ) Vis( l.second ).visible = true;
- }
- }
- ImGui::SameLine();
- if( ImGui::SmallButton( "Unselect all" ) )
- {
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->threadList.size() != 1 && l.second->isContended ) Vis( l.second ).visible = false;
- }
- }
-
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->valid && !l.second->timeline.empty() && l.second->threadList.size() != 1 && l.second->isContended )
- {
- auto& sl = m_worker.GetSourceLocation( l.second->srcloc );
- auto fileName = m_worker.GetString( sl.file );
-
- char buf[1024];
- if( l.second->customName.Active() )
- {
- sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( l.second->customName ) );
- }
- else
- {
- sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( m_worker.GetSourceLocation( l.second->srcloc ).function ) );
- }
- SmallCheckbox( buf, &Vis( l.second ).visible );
- if( ImGui::IsItemHovered() )
- {
- m_lockHoverHighlight = l.first;
-
- if( ImGui::IsItemClicked( 1 ) )
- {
- m_lockInfoWindow = l.first;
- }
- }
- if( m_optionsLockBuzzAnim.Match( l.second->srcloc ) )
- {
- const auto time = m_optionsLockBuzzAnim.Time();
- const auto indentVal = sin( time * 60.f ) * 10.f * time;
- ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
- }
- else
- {
- ImGui::SameLine();
- }
- ImGui::TextDisabled( "(%s) %s", RealToString( l.second->timeline.size() ), LocationToString( fileName, sl.line ) );
- if( ImGui::IsItemHovered() )
- {
- DrawSourceTooltip( fileName, sl.line, 1, 1 );
- if( ImGui::IsItemClicked( 1 ) )
- {
- if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
- {
- ViewSource( fileName, sl.line );
- }
- else
- {
- m_optionsLockBuzzAnim.Enable( l.second->srcloc, 0.5f );
- }
- }
- }
- }
- }
- ImGui::TreePop();
- }
- const bool multiUncontExpand = ImGui::TreeNodeEx( "Uncontended locks present in multiple threads", 0 );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%zu)", multiCntUncont );
- if( multiUncontExpand )
- {
- ImGui::SameLine();
- if( ImGui::SmallButton( "Select all" ) )
- {
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->threadList.size() != 1 && !l.second->isContended ) Vis( l.second ).visible = true;
- }
- }
- ImGui::SameLine();
- if( ImGui::SmallButton( "Unselect all" ) )
- {
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->threadList.size() != 1 && !l.second->isContended ) Vis( l.second ).visible = false;
- }
- }
-
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->valid && !l.second->timeline.empty() && l.second->threadList.size() != 1 && !l.second->isContended )
- {
- auto& sl = m_worker.GetSourceLocation( l.second->srcloc );
- auto fileName = m_worker.GetString( sl.file );
-
- char buf[1024];
- if( l.second->customName.Active() )
- {
- sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( l.second->customName ) );
- }
- else
- {
- sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( m_worker.GetSourceLocation( l.second->srcloc ).function ) );
- }
- SmallCheckbox( buf, &Vis( l.second ).visible );
- if( ImGui::IsItemHovered() )
- {
- m_lockHoverHighlight = l.first;
-
- if( ImGui::IsItemClicked( 1 ) )
- {
- m_lockInfoWindow = l.first;
- }
- }
- if( m_optionsLockBuzzAnim.Match( l.second->srcloc ) )
- {
- const auto time = m_optionsLockBuzzAnim.Time();
- const auto indentVal = sin( time * 60.f ) * 10.f * time;
- ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
- }
- else
- {
- ImGui::SameLine();
- }
- ImGui::TextDisabled( "(%s) %s", RealToString( l.second->timeline.size() ), LocationToString( fileName, sl.line ) );
- if( ImGui::IsItemHovered() )
- {
- DrawSourceTooltip( fileName, sl.line, 1, 1 );
- if( ImGui::IsItemClicked( 1 ) )
- {
- if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
- {
- ViewSource( fileName, sl.line );
- }
- else
- {
- m_optionsLockBuzzAnim.Enable( l.second->srcloc, 0.5f );
- }
- }
- }
- }
- }
- ImGui::TreePop();
- }
- const auto singleExpand = ImGui::TreeNodeEx( "Locks present in a single thread", 0 );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%zu)", singleCnt );
- if( singleExpand )
- {
- ImGui::SameLine();
- if( ImGui::SmallButton( "Select all" ) )
- {
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->threadList.size() == 1 ) Vis( l.second ).visible = true;
- }
- }
- ImGui::SameLine();
- if( ImGui::SmallButton( "Unselect all" ) )
- {
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->threadList.size() == 1 ) Vis( l.second ).visible = false;
- }
- }
-
- for( const auto& l : m_worker.GetLockMap() )
- {
- if( l.second->valid && !l.second->timeline.empty() && l.second->threadList.size() == 1 )
- {
- auto& sl = m_worker.GetSourceLocation( l.second->srcloc );
- auto fileName = m_worker.GetString( sl.file );
-
- char buf[1024];
- if( l.second->customName.Active() )
- {
- sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( l.second->customName ) );
- }
- else
- {
- sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( m_worker.GetSourceLocation( l.second->srcloc ).function ) );
- }
- SmallCheckbox( buf, &Vis( l.second ).visible );
- if( ImGui::IsItemHovered() )
- {
- m_lockHoverHighlight = l.first;
-
- if( ImGui::IsItemClicked( 1 ) )
- {
- m_lockInfoWindow = l.first;
- }
- }
- if( m_optionsLockBuzzAnim.Match( l.second->srcloc ) )
- {
- const auto time = m_optionsLockBuzzAnim.Time();
- const auto indentVal = sin( time * 60.f ) * 10.f * time;
- ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
- }
- else
- {
- ImGui::SameLine();
- }
- ImGui::TextDisabled( "(%s) %s", RealToString( l.second->timeline.size() ), LocationToString( fileName, sl.line ) );
- if( ImGui::IsItemHovered() )
- {
- DrawSourceTooltip( fileName, sl.line, 1, 1 );
- if( ImGui::IsItemClicked( 1 ) )
- {
- if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
- {
- ViewSource( fileName, sl.line );
- }
- else
- {
- m_optionsLockBuzzAnim.Enable( l.second->srcloc, 0.5f );
- }
- }
- }
- }
- }
- ImGui::TreePop();
- }
- ImGui::TreePop();
- }
- }
-
- if( !m_worker.GetPlots().empty() )
- {
- ImGui::Separator();
- val = m_vd.drawPlots;
- ImGui::Checkbox( ICON_FA_SIGNATURE " Draw plots", &val );
- m_vd.drawPlots = val;
- const auto expand = ImGui::TreeNode( "Plots" );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%zu)", m_worker.GetPlots().size() );
- if( expand )
- {
- ImGui::SameLine();
- if( ImGui::SmallButton( "Select all" ) )
- {
- for( const auto& p : m_worker.GetPlots() )
- {
- Vis( p ).visible = true;
- }
- }
- ImGui::SameLine();
- if( ImGui::SmallButton( "Unselect all" ) )
- {
- for( const auto& p : m_worker.GetPlots() )
- {
- Vis( p ).visible = false;
- }
- }
-
- for( const auto& p : m_worker.GetPlots() )
- {
- SmallCheckbox( GetPlotName( p ), &Vis( p ).visible );
- ImGui::SameLine();
- ImGui::TextDisabled( "%s data points", RealToString( p->data.size() ) );
- }
- ImGui::TreePop();
- }
- }
-
- ImGui::Separator();
- auto expand = ImGui::TreeNode( ICON_FA_RANDOM " Visible threads:" );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%zu)", m_threadOrder.size() );
- if( expand )
- {
- auto& crash = m_worker.GetCrashEvent();
-
- ImGui::SameLine();
- if( ImGui::SmallButton( "Select all" ) )
- {
- for( const auto& t : m_threadOrder )
- {
- Vis( t ).visible = true;
- }
- }
- ImGui::SameLine();
- if( ImGui::SmallButton( "Unselect all" ) )
- {
- for( const auto& t : m_threadOrder )
- {
- Vis( t ).visible = false;
- }
- }
-
- const auto wposx = ImGui::GetCursorScreenPos().x;
- m_threadDnd.clear();
- int idx = 0;
- for( const auto& t : m_threadOrder )
- {
- m_threadDnd.push_back( ImGui::GetCursorScreenPos().y );
- ImGui::PushID( idx );
- const auto threadName = m_worker.GetThreadName( t->id );
- const auto threadColor = GetThreadColor( t->id, 0 );
- SmallColorBox( threadColor );
- ImGui::SameLine();
- SmallCheckbox( threadName, &Vis( t ).visible );
- if( ImGui::BeginDragDropSource( ImGuiDragDropFlags_SourceNoHoldToOpenOthers ) )
- {
- ImGui::SetDragDropPayload( "ThreadOrder", &idx, sizeof(int) );
- ImGui::TextUnformatted( ICON_FA_RANDOM );
- ImGui::SameLine();
- SmallColorBox( threadColor );
- ImGui::SameLine();
- ImGui::TextUnformatted( threadName );
- ImGui::EndDragDropSource();
- }
- ImGui::PopID();
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", RealToString( t->id ) );
- if( crash.thread == t->id )
- {
- ImGui::SameLine();
- TextColoredUnformatted( ImVec4( 1.f, 0.2f, 0.2f, 1.f ), ICON_FA_SKULL );
- if( ImGui::IsItemHovered() )
- {
- ImGui::BeginTooltip();
- ImGui::TextUnformatted( "Crashed" );
- ImGui::EndTooltip();
- if( IsMouseClicked( 0 ) )
- {
- m_showInfo = true;
- }
- if( IsMouseClicked( 2 ) )
- {
- CenterAtTime( crash.time );
- }
- }
- }
- if( t->isFiber )
- {
- ImGui::SameLine();
- TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
- }
- ImGui::SameLine();
- ImGui::TextDisabled( "%s top level zones", RealToString( t->timeline.size() ) );
- idx++;
- }
- if( m_threadDnd.size() > 1 )
- {
- const auto w = ImGui::GetContentRegionAvail().x;
- const auto dist = m_threadDnd[1] - m_threadDnd[0];
- const auto half = dist * 0.5f;
- m_threadDnd.push_back( m_threadDnd.back() + dist );
-
- int target = -1;
- int source;
- for( size_t i=0; iAddLine( ImVec2( wposx, m_threadDnd[i] ), ImVec2( wposx + w, m_threadDnd[i] ), ImGui::GetColorU32(ImGuiCol_DragDropTarget), 2.f );
- if( auto payload = ImGui::AcceptDragDropPayload( "ThreadOrder", ImGuiDragDropFlags_AcceptNoDrawDefaultRect ) )
- {
- target = (int)i;
- source = *(int*)payload->Data;
- }
- ImGui::EndDragDropTarget();
- }
- }
- if( target >= 0 && target != source )
- {
- const auto srcval = m_threadOrder[source];
- if( target < source )
- {
- assert( source < (int)m_threadOrder.size() );
- m_threadOrder.erase( m_threadOrder.begin() + source );
- m_threadOrder.insert( m_threadOrder.begin() + target, srcval );
- }
- else
- {
- assert( target <= (int)m_threadOrder.size() );
- m_threadOrder.insert( m_threadOrder.begin() + target, srcval );
- m_threadOrder.erase( m_threadOrder.begin() + source );
- }
- }
- }
- ImGui::TreePop();
- }
-
- ImGui::Separator();
- expand = ImGui::TreeNode( ICON_FA_IMAGES " Visible frame sets:" );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%zu)", m_worker.GetFrames().size() );
- if( expand )
- {
- ImGui::SameLine();
- if( ImGui::SmallButton( "Select all" ) )
- {
- for( const auto& fd : m_worker.GetFrames() )
- {
- Vis( fd ).visible = true;
- }
- }
- ImGui::SameLine();
- if( ImGui::SmallButton( "Unselect all" ) )
- {
- for( const auto& fd : m_worker.GetFrames() )
- {
- Vis( fd ).visible = false;
- }
- }
-
- int idx = 0;
- for( const auto& fd : m_worker.GetFrames() )
- {
- ImGui::PushID( idx++ );
- SmallCheckbox( fd->name == 0 ? "Frames" : m_worker.GetString( fd->name ), &Vis( fd ).visible );
- ImGui::PopID();
- ImGui::SameLine();
- ImGui::TextDisabled( "%s %sframes", RealToString( fd->frames.size() ), fd->continuous ? "" : "discontinuous " );
- }
- ImGui::TreePop();
- }
- ImGui::End();
-}
-
void View::DrawMessages()
{
const auto& msgs = m_worker.GetMessages();
diff --git a/server/TracyView.hpp b/server/TracyView.hpp
index 52436d70..6c9d5656 100644
--- a/server/TracyView.hpp
+++ b/server/TracyView.hpp
@@ -29,6 +29,15 @@ struct ImFont;
namespace tracy
{
+constexpr const char* GpuContextNames[] = {
+ "Invalid",
+ "OpenGL",
+ "Vulkan",
+ "OpenCL",
+ "Direct3D 12",
+ "Direct3D 11"
+};
+
struct MemoryPage;
class FileRead;
class SourceView;
diff --git a/server/TracyView_Options.cpp b/server/TracyView_Options.cpp
new file mode 100644
index 00000000..8cba8fc9
--- /dev/null
+++ b/server/TracyView_Options.cpp
@@ -0,0 +1,711 @@
+#include
+#include
+
+#include "TracyFilesystem.hpp"
+#include "TracyMouse.hpp"
+#include "TracyPrint.hpp"
+#include "TracyView.hpp"
+
+namespace tracy
+{
+
+void View::DrawOptions()
+{
+ ImGui::Begin( "Options", &m_showOptions, ImGuiWindowFlags_AlwaysAutoResize );
+ if( ImGui::GetCurrentWindowRead()->SkipItems ) { ImGui::End(); return; }
+
+ const auto scale = GetScale();
+ bool val = m_vd.drawEmptyLabels;
+ ImGui::Checkbox( ICON_FA_EXPAND " Draw empty labels", &val );
+ m_vd.drawEmptyLabels = val;
+ val = m_vd.drawFrameTargets;
+ ImGui::Checkbox( ICON_FA_FLAG_CHECKERED " Draw frame targets", &val );
+ m_vd.drawFrameTargets = val;
+ ImGui::Indent();
+ int tmp = m_vd.frameTarget;
+ ImGui::SetNextItemWidth( 90 * scale );
+ if( ImGui::InputInt( "Target FPS", &tmp ) )
+ {
+ if( tmp < 1 ) tmp = 1;
+ m_vd.frameTarget = tmp;
+ }
+ ImGui::SameLine();
+ TextDisabledUnformatted( TimeToString( 1000*1000*1000 / tmp ) );
+ ImGui::Unindent();
+ if( m_worker.HasContextSwitches() )
+ {
+ ImGui::Separator();
+ val = m_vd.drawContextSwitches;
+ ImGui::Checkbox( ICON_FA_HIKING " Draw context switches", &val );
+ m_vd.drawContextSwitches = val;
+ ImGui::Indent();
+ val = m_vd.darkenContextSwitches;
+ SmallCheckbox( ICON_FA_MOON " Darken inactive threads", &val );
+ m_vd.darkenContextSwitches = val;
+ ImGui::Unindent();
+ val = m_vd.drawCpuData;
+ ImGui::Checkbox( ICON_FA_SLIDERS_H " Draw CPU data", &val );
+ m_vd.drawCpuData = val;
+ ImGui::Indent();
+ val = m_vd.drawCpuUsageGraph;
+ SmallCheckbox( ICON_FA_SIGNATURE " Draw CPU usage graph", &val );
+ m_vd.drawCpuUsageGraph = val;
+ ImGui::Unindent();
+ }
+
+ if( m_worker.GetCallstackSampleCount() != 0 )
+ {
+ val = m_vd.drawSamples;
+ ImGui::Checkbox( ICON_FA_EYE_DROPPER " Draw stack samples", &val );
+ m_vd.drawSamples = val;
+ }
+
+ const auto& gpuData = m_worker.GetGpuData();
+ if( !gpuData.empty() )
+ {
+ ImGui::Separator();
+ val = m_vd.drawGpuZones;
+ ImGui::Checkbox( ICON_FA_EYE " Draw GPU zones", &val );
+ m_vd.drawGpuZones = val;
+ const auto expand = ImGui::TreeNode( "GPU zones" );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%zu)", gpuData.size() );
+ if( expand )
+ {
+ for( size_t i=0; ithreadData.begin()->second.timeline;
+ char buf[1024];
+ sprintf( buf, "%s context %zu", GpuContextNames[(int)gpuData[i]->type], i );
+ SmallCheckbox( buf, &Vis( gpuData[i] ).visible );
+ ImGui::SameLine();
+ if( gpuData[i]->threadData.size() == 1 )
+ {
+ ImGui::TextDisabled( "%s top level zones", RealToString( timeline.size() ) );
+ }
+ else
+ {
+ ImGui::TextDisabled( "%s threads", RealToString( gpuData[i]->threadData.size() ) );
+ }
+ if( gpuData[i]->name.Active() )
+ {
+ ImGui::PushFont( m_smallFont );
+ TextFocused( "Name:", m_worker.GetString( gpuData[i]->name ) );
+ ImGui::PopFont();
+ }
+ if( !gpuData[i]->hasCalibration )
+ {
+ ImGui::TreePush();
+ auto& drift = GpuDrift( gpuData[i] );
+ ImGui::SetNextItemWidth( 120 * scale );
+ ImGui::PushID( i );
+ ImGui::InputInt( "Drift (ns/s)", &drift );
+ ImGui::PopID();
+ if( timeline.size() > 1 )
+ {
+ ImGui::SameLine();
+ if( ImGui::Button( ICON_FA_ROBOT " Auto" ) )
+ {
+ size_t lastidx = 0;
+ if( timeline.is_magic() )
+ {
+ auto& tl = *((Vector*)&timeline);
+ for( size_t j=tl.size()-1; j > 0; j-- )
+ {
+ if( tl[j].GpuEnd() >= 0 )
+ {
+ lastidx = j;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for( size_t j=timeline.size()-1; j > 0; j-- )
+ {
+ if( timeline[j]->GpuEnd() >= 0 )
+ {
+ lastidx = j;
+ break;
+ }
+ }
+ }
+
+ enum { NumSlopes = 10000 };
+ std::random_device rd;
+ std::default_random_engine gen( rd() );
+ std::uniform_int_distribution dist( 0, lastidx - 1 );
+ float slopes[NumSlopes];
+ size_t idx = 0;
+ if( timeline.is_magic() )
+ {
+ auto& tl = *((Vector*)&timeline);
+ do
+ {
+ const auto p0 = dist( gen );
+ const auto p1 = dist( gen );
+ if( p0 != p1 )
+ {
+ slopes[idx++] = float( 1.0 - double( tl[p1].GpuStart() - tl[p0].GpuStart() ) / double( tl[p1].CpuStart() - tl[p0].CpuStart() ) );
+ }
+ }
+ while( idx < NumSlopes );
+ }
+ else
+ {
+ do
+ {
+ const auto p0 = dist( gen );
+ const auto p1 = dist( gen );
+ if( p0 != p1 )
+ {
+ slopes[idx++] = float( 1.0 - double( timeline[p1]->GpuStart() - timeline[p0]->GpuStart() ) / double( timeline[p1]->CpuStart() - timeline[p0]->CpuStart() ) );
+ }
+ }
+ while( idx < NumSlopes );
+ }
+ std::sort( slopes, slopes+NumSlopes );
+ drift = int( 1000000000 * -slopes[NumSlopes/2] );
+ }
+ }
+ ImGui::TreePop();
+ }
+ }
+ ImGui::TreePop();
+ }
+ }
+
+ ImGui::Separator();
+ val = m_vd.drawZones;
+ ImGui::Checkbox( ICON_FA_MICROCHIP " Draw CPU zones", &val );
+ ImGui::Indent();
+ m_vd.drawZones = val;
+
+#ifndef TRACY_NO_STATISTICS
+ if( m_worker.AreGhostZonesReady() && m_worker.GetGhostZonesCount() != 0 )
+ {
+ val = m_vd.ghostZones;
+ SmallCheckbox( ICON_FA_GHOST " Draw ghost zones", &val );
+ m_vd.ghostZones = val;
+ }
+#endif
+
+ int ival = m_vd.dynamicColors;
+ ImGui::TextUnformatted( ICON_FA_PALETTE " Zone colors" );
+ ImGui::SameLine();
+ bool forceColors = m_vd.forceColors;
+ if( SmallCheckbox( "Ignore custom", &forceColors ) ) m_vd.forceColors = forceColors;
+ ImGui::Indent();
+ ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
+ ImGui::RadioButton( "Static", &ival, 0 );
+ ImGui::RadioButton( "Thread dynamic", &ival, 1 );
+ ImGui::RadioButton( "Source location dynamic", &ival, 2 );
+ ImGui::PopStyleVar();
+ ImGui::Unindent();
+ m_vd.dynamicColors = ival;
+ ival = (int)m_namespace;
+ ImGui::TextUnformatted( ICON_FA_BOX_OPEN " Namespaces" );
+ ImGui::Indent();
+ ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
+ ImGui::RadioButton( "Full", &ival, 0 );
+ ImGui::RadioButton( "Shortened", &ival, 1 );
+ ImGui::RadioButton( "None", &ival, 2 );
+ ImGui::PopStyleVar();
+ ImGui::Unindent();
+ m_namespace = (Namespace)ival;
+ ImGui::Unindent();
+
+ if( !m_worker.GetLockMap().empty() )
+ {
+ size_t lockCnt = 0;
+ size_t singleCnt = 0;
+ size_t multiCntCont = 0;
+ size_t multiCntUncont = 0;
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->valid && !l.second->timeline.empty() )
+ {
+ lockCnt++;
+ if( l.second->threadList.size() == 1 )
+ {
+ singleCnt++;
+ }
+ else if( l.second->isContended )
+ {
+ multiCntCont++;
+ }
+ else
+ {
+ multiCntUncont++;
+ }
+ }
+ }
+
+ ImGui::Separator();
+ val = m_vd.drawLocks;
+ ImGui::Checkbox( ICON_FA_LOCK " Draw locks", &val );
+ m_vd.drawLocks = val;
+ ImGui::SameLine();
+ val = m_vd.onlyContendedLocks;
+ ImGui::Checkbox( "Only contended", &val );
+ m_vd.onlyContendedLocks = val;
+ const auto expand = ImGui::TreeNode( "Locks" );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%zu)", lockCnt );
+ TooltipIfHovered( "Locks with no recorded events are counted, but not listed." );
+ if( expand )
+ {
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Select all" ) )
+ {
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ Vis( l.second ).visible = true;
+ }
+ }
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Unselect all" ) )
+ {
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ Vis( l.second ).visible = false;
+ }
+ }
+ ImGui::SameLine();
+ DrawHelpMarker( "Right click on lock name to open lock information window." );
+
+ const bool multiExpand = ImGui::TreeNodeEx( "Contended locks present in multiple threads", ImGuiTreeNodeFlags_DefaultOpen );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%zu)", multiCntCont );
+ if( multiExpand )
+ {
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Select all" ) )
+ {
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->threadList.size() != 1 && l.second->isContended ) Vis( l.second ).visible = true;
+ }
+ }
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Unselect all" ) )
+ {
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->threadList.size() != 1 && l.second->isContended ) Vis( l.second ).visible = false;
+ }
+ }
+
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->valid && !l.second->timeline.empty() && l.second->threadList.size() != 1 && l.second->isContended )
+ {
+ auto& sl = m_worker.GetSourceLocation( l.second->srcloc );
+ auto fileName = m_worker.GetString( sl.file );
+
+ char buf[1024];
+ if( l.second->customName.Active() )
+ {
+ sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( l.second->customName ) );
+ }
+ else
+ {
+ sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( m_worker.GetSourceLocation( l.second->srcloc ).function ) );
+ }
+ SmallCheckbox( buf, &Vis( l.second ).visible );
+ if( ImGui::IsItemHovered() )
+ {
+ m_lockHoverHighlight = l.first;
+
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ m_lockInfoWindow = l.first;
+ }
+ }
+ if( m_optionsLockBuzzAnim.Match( l.second->srcloc ) )
+ {
+ const auto time = m_optionsLockBuzzAnim.Time();
+ const auto indentVal = sin( time * 60.f ) * 10.f * time;
+ ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
+ }
+ else
+ {
+ ImGui::SameLine();
+ }
+ ImGui::TextDisabled( "(%s) %s", RealToString( l.second->timeline.size() ), LocationToString( fileName, sl.line ) );
+ if( ImGui::IsItemHovered() )
+ {
+ DrawSourceTooltip( fileName, sl.line, 1, 1 );
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
+ {
+ ViewSource( fileName, sl.line );
+ }
+ else
+ {
+ m_optionsLockBuzzAnim.Enable( l.second->srcloc, 0.5f );
+ }
+ }
+ }
+ }
+ }
+ ImGui::TreePop();
+ }
+ const bool multiUncontExpand = ImGui::TreeNodeEx( "Uncontended locks present in multiple threads", 0 );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%zu)", multiCntUncont );
+ if( multiUncontExpand )
+ {
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Select all" ) )
+ {
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->threadList.size() != 1 && !l.second->isContended ) Vis( l.second ).visible = true;
+ }
+ }
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Unselect all" ) )
+ {
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->threadList.size() != 1 && !l.second->isContended ) Vis( l.second ).visible = false;
+ }
+ }
+
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->valid && !l.second->timeline.empty() && l.second->threadList.size() != 1 && !l.second->isContended )
+ {
+ auto& sl = m_worker.GetSourceLocation( l.second->srcloc );
+ auto fileName = m_worker.GetString( sl.file );
+
+ char buf[1024];
+ if( l.second->customName.Active() )
+ {
+ sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( l.second->customName ) );
+ }
+ else
+ {
+ sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( m_worker.GetSourceLocation( l.second->srcloc ).function ) );
+ }
+ SmallCheckbox( buf, &Vis( l.second ).visible );
+ if( ImGui::IsItemHovered() )
+ {
+ m_lockHoverHighlight = l.first;
+
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ m_lockInfoWindow = l.first;
+ }
+ }
+ if( m_optionsLockBuzzAnim.Match( l.second->srcloc ) )
+ {
+ const auto time = m_optionsLockBuzzAnim.Time();
+ const auto indentVal = sin( time * 60.f ) * 10.f * time;
+ ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
+ }
+ else
+ {
+ ImGui::SameLine();
+ }
+ ImGui::TextDisabled( "(%s) %s", RealToString( l.second->timeline.size() ), LocationToString( fileName, sl.line ) );
+ if( ImGui::IsItemHovered() )
+ {
+ DrawSourceTooltip( fileName, sl.line, 1, 1 );
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
+ {
+ ViewSource( fileName, sl.line );
+ }
+ else
+ {
+ m_optionsLockBuzzAnim.Enable( l.second->srcloc, 0.5f );
+ }
+ }
+ }
+ }
+ }
+ ImGui::TreePop();
+ }
+ const auto singleExpand = ImGui::TreeNodeEx( "Locks present in a single thread", 0 );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%zu)", singleCnt );
+ if( singleExpand )
+ {
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Select all" ) )
+ {
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->threadList.size() == 1 ) Vis( l.second ).visible = true;
+ }
+ }
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Unselect all" ) )
+ {
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->threadList.size() == 1 ) Vis( l.second ).visible = false;
+ }
+ }
+
+ for( const auto& l : m_worker.GetLockMap() )
+ {
+ if( l.second->valid && !l.second->timeline.empty() && l.second->threadList.size() == 1 )
+ {
+ auto& sl = m_worker.GetSourceLocation( l.second->srcloc );
+ auto fileName = m_worker.GetString( sl.file );
+
+ char buf[1024];
+ if( l.second->customName.Active() )
+ {
+ sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( l.second->customName ) );
+ }
+ else
+ {
+ sprintf( buf, "%" PRIu32 ": %s", l.first, m_worker.GetString( m_worker.GetSourceLocation( l.second->srcloc ).function ) );
+ }
+ SmallCheckbox( buf, &Vis( l.second ).visible );
+ if( ImGui::IsItemHovered() )
+ {
+ m_lockHoverHighlight = l.first;
+
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ m_lockInfoWindow = l.first;
+ }
+ }
+ if( m_optionsLockBuzzAnim.Match( l.second->srcloc ) )
+ {
+ const auto time = m_optionsLockBuzzAnim.Time();
+ const auto indentVal = sin( time * 60.f ) * 10.f * time;
+ ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
+ }
+ else
+ {
+ ImGui::SameLine();
+ }
+ ImGui::TextDisabled( "(%s) %s", RealToString( l.second->timeline.size() ), LocationToString( fileName, sl.line ) );
+ if( ImGui::IsItemHovered() )
+ {
+ DrawSourceTooltip( fileName, sl.line, 1, 1 );
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
+ {
+ ViewSource( fileName, sl.line );
+ }
+ else
+ {
+ m_optionsLockBuzzAnim.Enable( l.second->srcloc, 0.5f );
+ }
+ }
+ }
+ }
+ }
+ ImGui::TreePop();
+ }
+ ImGui::TreePop();
+ }
+ }
+
+ if( !m_worker.GetPlots().empty() )
+ {
+ ImGui::Separator();
+ val = m_vd.drawPlots;
+ ImGui::Checkbox( ICON_FA_SIGNATURE " Draw plots", &val );
+ m_vd.drawPlots = val;
+ const auto expand = ImGui::TreeNode( "Plots" );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%zu)", m_worker.GetPlots().size() );
+ if( expand )
+ {
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Select all" ) )
+ {
+ for( const auto& p : m_worker.GetPlots() )
+ {
+ Vis( p ).visible = true;
+ }
+ }
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Unselect all" ) )
+ {
+ for( const auto& p : m_worker.GetPlots() )
+ {
+ Vis( p ).visible = false;
+ }
+ }
+
+ for( const auto& p : m_worker.GetPlots() )
+ {
+ SmallCheckbox( GetPlotName( p ), &Vis( p ).visible );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "%s data points", RealToString( p->data.size() ) );
+ }
+ ImGui::TreePop();
+ }
+ }
+
+ ImGui::Separator();
+ auto expand = ImGui::TreeNode( ICON_FA_RANDOM " Visible threads:" );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%zu)", m_threadOrder.size() );
+ if( expand )
+ {
+ auto& crash = m_worker.GetCrashEvent();
+
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Select all" ) )
+ {
+ for( const auto& t : m_threadOrder )
+ {
+ Vis( t ).visible = true;
+ }
+ }
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Unselect all" ) )
+ {
+ for( const auto& t : m_threadOrder )
+ {
+ Vis( t ).visible = false;
+ }
+ }
+
+ const auto wposx = ImGui::GetCursorScreenPos().x;
+ m_threadDnd.clear();
+ int idx = 0;
+ for( const auto& t : m_threadOrder )
+ {
+ m_threadDnd.push_back( ImGui::GetCursorScreenPos().y );
+ ImGui::PushID( idx );
+ const auto threadName = m_worker.GetThreadName( t->id );
+ const auto threadColor = GetThreadColor( t->id, 0 );
+ SmallColorBox( threadColor );
+ ImGui::SameLine();
+ SmallCheckbox( threadName, &Vis( t ).visible );
+ if( ImGui::BeginDragDropSource( ImGuiDragDropFlags_SourceNoHoldToOpenOthers ) )
+ {
+ ImGui::SetDragDropPayload( "ThreadOrder", &idx, sizeof(int) );
+ ImGui::TextUnformatted( ICON_FA_RANDOM );
+ ImGui::SameLine();
+ SmallColorBox( threadColor );
+ ImGui::SameLine();
+ ImGui::TextUnformatted( threadName );
+ ImGui::EndDragDropSource();
+ }
+ ImGui::PopID();
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", RealToString( t->id ) );
+ if( crash.thread == t->id )
+ {
+ ImGui::SameLine();
+ TextColoredUnformatted( ImVec4( 1.f, 0.2f, 0.2f, 1.f ), ICON_FA_SKULL );
+ if( ImGui::IsItemHovered() )
+ {
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted( "Crashed" );
+ ImGui::EndTooltip();
+ if( IsMouseClicked( 0 ) )
+ {
+ m_showInfo = true;
+ }
+ if( IsMouseClicked( 2 ) )
+ {
+ CenterAtTime( crash.time );
+ }
+ }
+ }
+ if( t->isFiber )
+ {
+ ImGui::SameLine();
+ TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
+ }
+ ImGui::SameLine();
+ ImGui::TextDisabled( "%s top level zones", RealToString( t->timeline.size() ) );
+ idx++;
+ }
+ if( m_threadDnd.size() > 1 )
+ {
+ const auto w = ImGui::GetContentRegionAvail().x;
+ const auto dist = m_threadDnd[1] - m_threadDnd[0];
+ const auto half = dist * 0.5f;
+ m_threadDnd.push_back( m_threadDnd.back() + dist );
+
+ int target = -1;
+ int source;
+ for( size_t i=0; iAddLine( ImVec2( wposx, m_threadDnd[i] ), ImVec2( wposx + w, m_threadDnd[i] ), ImGui::GetColorU32(ImGuiCol_DragDropTarget), 2.f );
+ if( auto payload = ImGui::AcceptDragDropPayload( "ThreadOrder", ImGuiDragDropFlags_AcceptNoDrawDefaultRect ) )
+ {
+ target = (int)i;
+ source = *(int*)payload->Data;
+ }
+ ImGui::EndDragDropTarget();
+ }
+ }
+ if( target >= 0 && target != source )
+ {
+ const auto srcval = m_threadOrder[source];
+ if( target < source )
+ {
+ assert( source < (int)m_threadOrder.size() );
+ m_threadOrder.erase( m_threadOrder.begin() + source );
+ m_threadOrder.insert( m_threadOrder.begin() + target, srcval );
+ }
+ else
+ {
+ assert( target <= (int)m_threadOrder.size() );
+ m_threadOrder.insert( m_threadOrder.begin() + target, srcval );
+ m_threadOrder.erase( m_threadOrder.begin() + source );
+ }
+ }
+ }
+ ImGui::TreePop();
+ }
+
+ ImGui::Separator();
+ expand = ImGui::TreeNode( ICON_FA_IMAGES " Visible frame sets:" );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%zu)", m_worker.GetFrames().size() );
+ if( expand )
+ {
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Select all" ) )
+ {
+ for( const auto& fd : m_worker.GetFrames() )
+ {
+ Vis( fd ).visible = true;
+ }
+ }
+ ImGui::SameLine();
+ if( ImGui::SmallButton( "Unselect all" ) )
+ {
+ for( const auto& fd : m_worker.GetFrames() )
+ {
+ Vis( fd ).visible = false;
+ }
+ }
+
+ int idx = 0;
+ for( const auto& fd : m_worker.GetFrames() )
+ {
+ ImGui::PushID( idx++ );
+ SmallCheckbox( fd->name == 0 ? "Frames" : m_worker.GetString( fd->name ), &Vis( fd ).visible );
+ ImGui::PopID();
+ ImGui::SameLine();
+ ImGui::TextDisabled( "%s %sframes", RealToString( fd->frames.size() ), fd->continuous ? "" : "discontinuous " );
+ }
+ ImGui::TreePop();
+ }
+ ImGui::End();
+}
+
+}