diff --git a/server/TracyTimelineController.cpp b/server/TracyTimelineController.cpp index e7efe3f37c..5161fa29f1 100644 --- a/server/TracyTimelineController.cpp +++ b/server/TracyTimelineController.cpp @@ -1,3 +1,5 @@ +#include + #include "imgui.h" #include "TracyTimelineController.hpp" @@ -8,6 +10,8 @@ namespace tracy TimelineController::TimelineController( View& view, Worker& worker ) : m_height( 0 ) , m_scroll( 0 ) + , m_centerItemkey( nullptr ) + , m_centerItemOffsetY( 0 ) , m_firstFrame( true ) , m_view( view ) , m_worker( worker ) @@ -24,17 +28,107 @@ void TimelineController::Begin() m_items.clear(); } -void TimelineController::End( double pxns, int offset, const ImVec2& wpos, bool hover, float yMin, float yMax ) +void TimelineController::UpdateCenterItem() { + ImVec2 mousePos = ImGui::GetMousePos(); + + m_centerItemkey = nullptr; + m_centerItemOffsetY = 0; + + if( m_firstFrame || !ImGui::IsMousePosValid( &mousePos ) ) return; + + const auto timelineMousePosY = mousePos.y - ImGui::GetWindowPos().y; + int centerY = timelineMousePosY + ImGui::GetScrollY(); + + int yBegin = 0; + int yEnd = 0; for( auto& item : m_items ) { - item->Draw( m_firstFrame, pxns, offset, wpos, hover, yMin, yMax ); + m_centerItemkey = item->GetKey(); + yBegin = yEnd; + yEnd += item->GetNextFrameHeight(); + + const auto inLowerBounds = m_centerItemkey == m_items.front()->GetKey() || yBegin <= centerY; + const auto inUpperBounds = m_centerItemkey == m_items.back()->GetKey() || centerY < yEnd; + + if( inLowerBounds && inUpperBounds ) + { + m_centerItemOffsetY = centerY - yBegin; + break; + } + } +} + +std::optional TimelineController::CalculateScrollPosition() const +{ + if( !m_centerItemkey ) return std::nullopt; + + ImVec2 mousePos = ImGui::GetMousePos(); + + if( !ImGui::IsMousePosValid( &mousePos ) ) return std::nullopt; + + const auto timelineMousePosY = mousePos.y - ImGui::GetWindowPos().y; + + int yBegin = 0; + int yEnd = 0; + for( auto& item : m_items ) + { + yBegin = yEnd; + yEnd += item->GetNextFrameHeight(); + + if( item->GetKey() != m_centerItemkey ) continue; + + int scrollY = yBegin + m_centerItemOffsetY - timelineMousePosY; + + return scrollY; + } + + return std::nullopt; +} + +void TimelineController::End( double pxns, const ImVec2& wpos, bool hover, bool vcenter, float yMin, float yMax ) +{ + auto shouldUpdateCenterItem = [&] () { + const auto imguiChangedScroll = m_scroll != ImGui::GetScrollY(); + const auto& mouseDelta = ImGui::GetIO().MouseDelta; + const auto mouseMoved = mouseDelta.x != 0.0f || mouseDelta.y != 0.0f; + const auto& mousePos = ImGui::GetIO().MousePos; + const auto mouseVisible = ImGui::IsMousePosValid( &mousePos ); + return ( ( imguiChangedScroll || mouseMoved || !mouseVisible ) && !ImGui::IsMouseDown( 1 ) ) || !m_centerItemkey; + }; + + if( !vcenter ) + { + m_centerItemkey = nullptr; + m_centerItemOffsetY = 0; + } + else if( shouldUpdateCenterItem() ) + { + UpdateCenterItem(); + } + + int yOffset = 0; + + for( auto& item : m_items ) + { + auto currentFrameItemHeight = item->GetNextFrameHeight(); + item->Draw( m_firstFrame, pxns, yOffset, wpos, hover, yMin, yMax ); + if( m_firstFrame ) currentFrameItemHeight = item->GetNextFrameHeight(); + yOffset += currentFrameItemHeight; + } + + if( const auto scrollY = CalculateScrollPosition() ) + { + int clampedScrollY = std::min( *scrollY, yOffset ); + ImGui::SetScrollY( clampedScrollY ); + int minHeight = ImGui::GetWindowHeight() + clampedScrollY; + yOffset = std::max( yOffset, minHeight ); } const auto scrollPos = ImGui::GetScrollY(); - if( ( scrollPos == 0 && m_scroll != 0 ) || offset > m_height ) + if( ( scrollPos == 0 && m_scroll != 0 ) || yOffset > m_height ) { - m_height = offset; + m_height = yOffset; } m_scroll = scrollPos; } diff --git a/server/TracyTimelineController.hpp b/server/TracyTimelineController.hpp index 1d8e4f3724..b14dca67e3 100644 --- a/server/TracyTimelineController.hpp +++ b/server/TracyTimelineController.hpp @@ -2,6 +2,7 @@ #define __TRACYTIMELINECONTROLLER_HPP__ #include +#include #include #include "../public/common/TracyForceInline.hpp" @@ -18,7 +19,7 @@ class TimelineController void FirstFrameExpired(); void Begin(); - void End( double pxns, int offset, const ImVec2& wpos, bool hover, float yMin, float yMax ); + void End( double pxns, const ImVec2& wpos, bool hover, bool vcenter, float yMin, float yMax ); template void AddItem( U* data ) @@ -39,12 +40,18 @@ class TimelineController } private: + void UpdateCenterItem(); + std::optional CalculateScrollPosition() const; + std::vector m_items; unordered_flat_map> m_itemMap; float m_height; float m_scroll; + const void* m_centerItemkey; + int m_centerItemOffsetY; + bool m_firstFrame; View& m_view; diff --git a/server/TracyTimelineItem.cpp b/server/TracyTimelineItem.cpp index 747be77b3d..c934fb7e6d 100644 --- a/server/TracyTimelineItem.cpp +++ b/server/TracyTimelineItem.cpp @@ -8,20 +8,24 @@ namespace tracy { -TimelineItem::TimelineItem( View& view, Worker& worker ) +TimelineItem::TimelineItem( View& view, Worker& worker, const void* key ) : m_visible( true ) , m_showFull( true ) , m_height( 0 ) + , m_key( key ) , m_view( view ) , m_worker( worker ) { } -void TimelineItem::Draw( bool firstFrame, double pxns, int& offset, const ImVec2& wpos, bool hover, float yMin, float yMax ) +void TimelineItem::Draw( bool firstFrame, double pxns, int yOffset, const ImVec2& wpos, bool hover, float yMin, float yMax ) { + const auto yBegin = yOffset; + auto yEnd = yOffset; + if( !IsVisible() ) { - if( m_height != 0 ) AdjustThreadHeight( firstFrame, offset, offset ); + if( m_height != 0 ) AdjustThreadHeight( firstFrame, yBegin, yEnd ); return; } if( IsEmpty() ) return; @@ -29,32 +33,31 @@ void TimelineItem::Draw( bool firstFrame, double pxns, int& offset, const ImVec2 const auto w = ImGui::GetContentRegionAvail().x - 1; const auto ty = ImGui::GetTextLineHeight(); const auto ostep = ty + 1; - const auto yPos = wpos.y + offset; + const auto yPos = wpos.y + yBegin; const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); - const auto oldOffset = offset; auto draw = ImGui::GetWindowDrawList(); ImGui::PushID( this ); - ImGui::PushClipRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + m_height ), true ); + ImGui::PushClipRect( wpos + ImVec2( 0, yBegin ), wpos + ImVec2( w, yBegin + m_height ), true ); - offset += ostep; + yEnd += ostep; if( m_showFull ) { - if( !DrawContents( pxns, offset, wpos, hover, yMin, yMax ) && !m_view.GetViewData().drawEmptyLabels ) + if( !DrawContents( pxns, yEnd, wpos, hover, yMin, yMax ) && !m_view.GetViewData().drawEmptyLabels ) { - offset = oldOffset; - AdjustThreadHeight( firstFrame, oldOffset, offset ); + yEnd = yBegin; + AdjustThreadHeight( firstFrame, yBegin, yEnd ); ImGui::PopClipRect(); ImGui::PopID(); return; } } - DrawOverlay( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ) ); + DrawOverlay( wpos + ImVec2( 0, yBegin ), wpos + ImVec2( w, yEnd ) ); ImGui::PopClipRect(); float labelWidth; - const auto hdrOffset = oldOffset; + const auto hdrOffset = yBegin; const bool drawHeader = yPos + ty >= yMin && yPos <= yMax; if( drawHeader ) { @@ -112,39 +115,38 @@ void TimelineItem::Draw( bool firstFrame, double pxns, int& offset, const ImVec2 ImGui::EndPopup(); } - offset += 0.2f * ostep; - AdjustThreadHeight( firstFrame, oldOffset, offset ); + yEnd += 0.2f * ostep; + AdjustThreadHeight( firstFrame, yBegin, yEnd ); ImGui::PopID(); } -void TimelineItem::AdjustThreadHeight( bool firstFrame, int oldOffset, int& offset ) +void TimelineItem::AdjustThreadHeight( bool firstFrame, int yBegin, int yEnd ) { const auto speed = 4.0; const auto baseMove = 1.0; - const auto h = offset - oldOffset; + const auto newHeight = yEnd - yBegin; if( firstFrame ) { - m_height = h; + m_height = newHeight; } - else if( m_height != h ) + else if( m_height != newHeight ) { - const auto diff = h - m_height; + const auto diff = newHeight - m_height; const auto preClampMove = diff * speed * ImGui::GetIO().DeltaTime; if( diff > 0 ) { const auto move = preClampMove + baseMove; - m_height = int( std::min( m_height + move, h ) ); + m_height = int( std::min( m_height + move, newHeight ) ); } else { const auto move = preClampMove - baseMove; - m_height = int( std::max( m_height + move, h ) ); + m_height = int( std::max( m_height + move, newHeight ) ); } s_wasActive = true; } - offset = oldOffset + m_height; } void TimelineItem::VisibilityCheckbox() diff --git a/server/TracyTimelineItem.hpp b/server/TracyTimelineItem.hpp index 54e154aae2..068bf1f1f9 100644 --- a/server/TracyTimelineItem.hpp +++ b/server/TracyTimelineItem.hpp @@ -14,10 +14,11 @@ class Worker; class TimelineItem { public: - TimelineItem( View& view, Worker& worker ); + TimelineItem( View& view, Worker& worker, const void* key ); virtual ~TimelineItem() = default; - void Draw( bool firstFrame, double pxns, int& offset, const ImVec2& wpos, bool hover, float yMin, float yMax ); + // draws the timeilne item and also updates the next frame height value + void Draw( bool firstFrame, double pxns, int yOffset, const ImVec2& wpos, bool hover, float yMin, float yMax ); void VisibilityCheckbox(); virtual void SetVisible( bool visible ) { m_visible = visible; } @@ -25,6 +26,11 @@ class TimelineItem void SetShowFull( bool showFull ) { m_showFull = showFull; } + // returns 0 instead of the correct value for the first frame + int GetNextFrameHeight() const { return m_height; } + + const void* GetKey() const { return m_key; } + protected: virtual uint32_t HeaderColor() const = 0; virtual uint32_t HeaderColorInactive() const = 0; @@ -46,10 +52,12 @@ class TimelineItem bool m_showFull; private: - void AdjustThreadHeight( bool firstFrame, int oldOffset, int& offset ); + void AdjustThreadHeight( bool firstFrame, int yBegin, int yEnd ); int m_height; + const void* m_key; + protected: View& m_view; Worker& m_worker; diff --git a/server/TracyTimelineItemCpuData.cpp b/server/TracyTimelineItemCpuData.cpp index 9664c10974..1701889fbb 100644 --- a/server/TracyTimelineItemCpuData.cpp +++ b/server/TracyTimelineItemCpuData.cpp @@ -8,8 +8,8 @@ namespace tracy { -TimelineItemCpuData::TimelineItemCpuData( View& view, Worker& worker, void* ) - : TimelineItem( view, worker ) +TimelineItemCpuData::TimelineItemCpuData( View& view, Worker& worker, void* key ) + : TimelineItem( view, worker, key ) { } diff --git a/server/TracyTimelineItemCpuData.hpp b/server/TracyTimelineItemCpuData.hpp index 642ba52cf2..399667a93a 100644 --- a/server/TracyTimelineItemCpuData.hpp +++ b/server/TracyTimelineItemCpuData.hpp @@ -10,7 +10,7 @@ namespace tracy class TimelineItemCpuData final : public TimelineItem { public: - TimelineItemCpuData( View& view, Worker& worker, void* ); + TimelineItemCpuData( View& view, Worker& worker, void* key ); void SetVisible( bool visible ) override; bool IsVisible() const override; diff --git a/server/TracyTimelineItemGpu.cpp b/server/TracyTimelineItemGpu.cpp index 11506e9b3f..2974eb5636 100644 --- a/server/TracyTimelineItemGpu.cpp +++ b/server/TracyTimelineItemGpu.cpp @@ -10,7 +10,7 @@ namespace tracy { TimelineItemGpu::TimelineItemGpu( View& view, Worker& worker, GpuCtxData* gpu ) - : TimelineItem( view, worker ) + : TimelineItem( view, worker, gpu ) , m_gpu( gpu ) , m_idx( view.GetNextGpuIdx() ) { diff --git a/server/TracyTimelineItemPlot.cpp b/server/TracyTimelineItemPlot.cpp index 07e45ef18b..6580d44a85 100644 --- a/server/TracyTimelineItemPlot.cpp +++ b/server/TracyTimelineItemPlot.cpp @@ -9,7 +9,7 @@ namespace tracy { TimelineItemPlot::TimelineItemPlot( View& view, Worker& worker, PlotData* plot ) - : TimelineItem( view, worker ) + : TimelineItem( view, worker, plot ) , m_plot( plot ) { } diff --git a/server/TracyTimelineItemThread.cpp b/server/TracyTimelineItemThread.cpp index c100bd5c76..b0f7f78558 100644 --- a/server/TracyTimelineItemThread.cpp +++ b/server/TracyTimelineItemThread.cpp @@ -12,7 +12,7 @@ namespace tracy { TimelineItemThread::TimelineItemThread( View& view, Worker& worker, const ThreadData* thread ) - : TimelineItem( view, worker ) + : TimelineItem( view, worker, thread ) , m_thread( thread ) , m_ghost( false ) { diff --git a/server/TracyView_Timeline.cpp b/server/TracyView_Timeline.cpp index 406a945b46..18241db657 100644 --- a/server/TracyView_Timeline.cpp +++ b/server/TracyView_Timeline.cpp @@ -321,19 +321,24 @@ void View::DrawTimeline() const auto yMin = ImGui::GetCursorScreenPos().y; const auto yMax = linepos.y + lineh; + ImGui::SetNextWindowContentSize( ImVec2( 0, m_tc.GetHeight() ) ); ImGui::BeginChild( "##zoneWin", ImVec2( ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y ), false, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoScrollWithMouse ); + const auto verticallyCenterTimeline = true; + if( m_yDelta != 0 ) { auto& io = ImGui::GetIO(); - auto y = ImGui::GetScrollY(); - ImGui::SetScrollY( y - m_yDelta ); + if( !verticallyCenterTimeline ) + { + auto y = ImGui::GetScrollY(); + ImGui::SetScrollY( y - m_yDelta ); + } io.MouseClickedPos[1].y = io.MousePos.y; } const auto wpos = ImGui::GetCursorScreenPos(); const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); - // note that m_tc.GetHeight() returns the height from the previous draw const auto h = std::max( m_tc.GetHeight(), ImGui::GetContentRegionAvail().y - 4 ); // magic border value ImGui::ItemSize( ImVec2( w, h ) ); @@ -341,7 +346,6 @@ void View::DrawTimeline() draw = ImGui::GetWindowDrawList(); const auto ty = ImGui::GetTextLineHeight(); - int offset = 0; const auto to = 9.f; const auto th = ( ty - to ) * sqrt( 3 ) * 0.5; @@ -381,7 +385,8 @@ void View::DrawTimeline() } } - m_tc.End( pxns, offset, wpos, hover, yMin, yMax ); + const auto vcenter = verticallyCenterTimeline && drawMouseLine && m_viewMode == ViewMode::Paused; + m_tc.End( pxns, wpos, hover, vcenter, yMin, yMax ); ImGui::EndChild(); m_lockHighlight = m_nextLockHighlight;