From 60579d6334f03b51d85805dcacba43897dbfbbeb Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Sun, 4 Sep 2022 13:38:19 +0200 Subject: [PATCH] Migrate drawing GPU data to the new timeline item system. --- profiler/build/win32/Tracy.vcxproj | 3 + profiler/build/win32/Tracy.vcxproj.filters | 9 + server/TracyTimelineItemGpu.cpp | 195 ++++++++++++ server/TracyTimelineItemGpu.hpp | 38 +++ server/TracyView.hpp | 1 + server/TracyView_GpuTimeline.cpp | 354 +++++++++++++++++++++ server/TracyView_Timeline.cpp | 286 +---------------- server/TracyView_ZoneTimeline.cpp | 262 --------------- 8 files changed, 603 insertions(+), 545 deletions(-) create mode 100644 server/TracyTimelineItemGpu.cpp create mode 100644 server/TracyTimelineItemGpu.hpp create mode 100644 server/TracyView_GpuTimeline.cpp diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj index 449381d5..d582aa87 100644 --- a/profiler/build/win32/Tracy.vcxproj +++ b/profiler/build/win32/Tracy.vcxproj @@ -138,6 +138,7 @@ + @@ -153,6 +154,7 @@ + @@ -277,6 +279,7 @@ + diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters index 591fd453..c362dd55 100644 --- a/profiler/build/win32/Tracy.vcxproj.filters +++ b/profiler/build/win32/Tracy.vcxproj.filters @@ -366,6 +366,12 @@ server + + server + + + server + @@ -743,6 +749,9 @@ server + + server + diff --git a/server/TracyTimelineItemGpu.cpp b/server/TracyTimelineItemGpu.cpp new file mode 100644 index 00000000..52924b5c --- /dev/null +++ b/server/TracyTimelineItemGpu.cpp @@ -0,0 +1,195 @@ +#include "TracyImGui.hpp" +#include "TracyPopcnt.hpp" +#include "TracyPrint.hpp" +#include "TracyTimelineItemGpu.hpp" +#include "TracyUtility.hpp" +#include "TracyView.hpp" +#include "TracyWorker.hpp" + +namespace tracy +{ + +TimelineItemGpu::TimelineItemGpu( View& view, Worker& worker, GpuCtxData* gpu ) + : TimelineItem( view, worker ) + , m_gpu( gpu ) + , m_idx( view.GetNextGpuIdx() ) +{ +} + +bool TimelineItemGpu::IsEmpty() const +{ + return m_gpu->threadData.empty(); +} + +const char* TimelineItemGpu::HeaderLabel() const +{ + static char buf[4096]; + if( m_gpu->name.Active() ) + { + sprintf( buf, "%s context %i: %s", GpuContextNames[(int)m_gpu->type], m_idx, m_worker.GetString( m_gpu->name ) ); + } + else + { + sprintf( buf, "%s context %i", GpuContextNames[(int)m_gpu->type], m_idx ); + } + return buf; +} + +void TimelineItemGpu::HeaderTooltip( const char* label ) const +{ + const bool dynamicColors = m_view.GetViewData().dynamicColors; + const bool isMultithreaded = + ( m_gpu->type == GpuContextType::Vulkan ) || + ( m_gpu->type == GpuContextType::OpenCL ) || + ( m_gpu->type == GpuContextType::Direct3D12 ); + + char buf[64]; + sprintf( buf, "%s context %i", GpuContextNames[(int)m_gpu->type], m_idx ); + + ImGui::BeginTooltip(); + ImGui::TextUnformatted( buf ); + if( m_gpu->name.Active() ) TextFocused( "Name:", m_worker.GetString( m_gpu->name ) ); + ImGui::Separator(); + if( !isMultithreaded ) + { + SmallColorBox( GetThreadColor( m_gpu->thread, 0, dynamicColors ) ); + ImGui::SameLine(); + TextFocused( "Thread:", m_worker.GetThreadName( m_gpu->thread ) ); + } + else + { + if( m_gpu->threadData.size() == 1 ) + { + auto it = m_gpu->threadData.begin(); + auto tid = it->first; + if( tid == 0 ) + { + if( !it->second.timeline.empty() ) + { + if( it->second.timeline.is_magic() ) + { + auto& tl = *(Vector*)&it->second.timeline; + tid = m_worker.DecompressThread( tl.begin()->Thread() ); + } + else + { + tid = m_worker.DecompressThread( (*it->second.timeline.begin())->Thread() ); + } + } + } + SmallColorBox( GetThreadColor( tid, 0, dynamicColors ) ); + ImGui::SameLine(); + TextFocused( "Thread:", m_worker.GetThreadName( tid ) ); + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( tid ) ); + if( m_worker.IsThreadFiber( tid ) ) + { + ImGui::SameLine(); + TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" ); + } + } + else + { + ImGui::TextDisabled( "Threads:" ); + ImGui::Indent(); + for( auto& td : m_gpu->threadData ) + { + SmallColorBox( GetThreadColor( td.first, 0, dynamicColors ) ); + ImGui::SameLine(); + ImGui::TextUnformatted( m_worker.GetThreadName( td.first ) ); + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( td.first ) ); + } + ImGui::Unindent(); + } + } + const auto t0 = RangeBegin(); + if( t0 != std::numeric_limits::max() ) + { + TextFocused( "Appeared at", TimeToString( t0 ) ); + } + TextFocused( "Zone count:", RealToString( m_gpu->count ) ); + if( m_gpu->period != 1.f ) + { + TextFocused( "Timestamp accuracy:", TimeToString( m_gpu->period ) ); + } + if( m_gpu->overflow != 0 ) + { + ImGui::Separator(); + ImGui::TextUnformatted( "GPU timer overflow has been detected." ); + TextFocused( "Timer resolution:", RealToString( 63 - TracyLzcnt( m_gpu->overflow ) ) ); + ImGui::SameLine(); + TextDisabledUnformatted( "bits" ); + } + ImGui::EndTooltip(); +} + +void TimelineItemGpu::HeaderExtraContents( int offset, const ImVec2& wpos, float labelWidth, double pxns, bool hover ) +{ + auto draw = ImGui::GetWindowDrawList(); + const auto ty = ImGui::GetTextLineHeight(); + + /* + char tmp[128]; + sprintf( tmp, "(y-range: %s, visible data points: %s)", FormatPlotValue( m_plot->rMax - m_plot->rMin, m_plot->format ), RealToString( m_plot->num ) ); + draw->AddText( wpos + ImVec2( ty * 1.5f + labelWidth, offset ), 0xFF226E6E, tmp ); + */ +} + +int64_t TimelineItemGpu::RangeBegin() const +{ + int64_t t = std::numeric_limits::max(); + for( auto& td : m_gpu->threadData ) + { + int64_t t0; + if( td.second.timeline.is_magic() ) + { + t0 = ((Vector*)&td.second.timeline)->front().GpuStart(); + } + else + { + t0 = td.second.timeline.front()->GpuStart(); + } + if( t0 >= 0 ) + { + t = std::min( t, t0 ); + } + } + return t; +} + +int64_t TimelineItemGpu::RangeEnd() const +{ + int64_t t = std::numeric_limits::min(); + for( auto& td : m_gpu->threadData ) + { + int64_t t0; + if( td.second.timeline.is_magic() ) + { + t0 = ((Vector*)&td.second.timeline)->front().GpuStart(); + } + else + { + t0 = td.second.timeline.front()->GpuStart(); + } + if( t0 >= 0 ) + { + if( td.second.timeline.is_magic() ) + { + t = std::max( t, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( ((Vector*)&td.second.timeline)->back() ) ) ); + } + else + { + t = std::max( t, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( *td.second.timeline.back() ) ) ); + } + } + } + return t; +} + +bool TimelineItemGpu::DrawContents( double pxns, int& offset, const ImVec2& wpos, bool hover, float yMin, float yMax ) +{ + return m_view.DrawGpu( *m_gpu, pxns, offset, wpos, hover, yMin, yMax ); +} + +} diff --git a/server/TracyTimelineItemGpu.hpp b/server/TracyTimelineItemGpu.hpp new file mode 100644 index 00000000..e538ccf3 --- /dev/null +++ b/server/TracyTimelineItemGpu.hpp @@ -0,0 +1,38 @@ +#ifndef __TRACYTIMELINEITEMGPU_HPP__ +#define __TRACYTIMELINEITEMGPU_HPP__ + +#include "TracyEvent.hpp" +#include "TracyTimelineItem.hpp" + +namespace tracy +{ + +class TimelineItemGpu final : public TimelineItem +{ +public: + TimelineItemGpu( View& view, Worker& worker, GpuCtxData* gpu ); + +protected: + uint32_t HeaderColor() const override { return 0xFFFFAAAA; } + uint32_t HeaderColorInactive() const override { return 0xFF886666; } + uint32_t HeaderLineColor() const override { return 0x33FFFFFF; } + const char* HeaderLabel() const override; + + int64_t RangeBegin() const override; + int64_t RangeEnd() const override; + + void HeaderTooltip( const char* label ) const override; + void HeaderExtraContents( int offset, const ImVec2& wpos, float labelWidth, double pxns, bool hover ) override; + + bool DrawContents( double pxns, int& offset, const ImVec2& wpos, bool hover, float yMin, float yMax ) override; + + bool IsEmpty() const override; + +private: + GpuCtxData* m_gpu; + int m_idx; +}; + +} + +#endif diff --git a/server/TracyView.hpp b/server/TracyView.hpp index ce4788c0..e8a48d1a 100644 --- a/server/TracyView.hpp +++ b/server/TracyView.hpp @@ -120,6 +120,7 @@ public: bool DrawThread( const ThreadData& thread, double pxns, int& offset, const ImVec2& wpos, bool hover, float yMin, float yMax, bool ghostMode ); void DrawThreadMessages( const ThreadData& thread, double pxns, int offset, const ImVec2& wpos, bool hover ); void DrawThreadOverlays( const ThreadData& thread, const ImVec2& ul, const ImVec2& dr ); + bool DrawGpu( const GpuCtxData& gpu, double pxns, int& offset, const ImVec2& wpos, bool hover, float yMin, float yMax ); bool m_showRanges = false; Range m_statRange; diff --git a/server/TracyView_GpuTimeline.cpp b/server/TracyView_GpuTimeline.cpp new file mode 100644 index 00000000..677a0174 --- /dev/null +++ b/server/TracyView_GpuTimeline.cpp @@ -0,0 +1,354 @@ +#include "TracyColor.hpp" +#include "TracyImGui.hpp" +#include "TracyMouse.hpp" +#include "TracyPrint.hpp" +#include "TracyView.hpp" + +namespace tracy +{ + +constexpr float MinVisSize = 3; + +bool View::DrawGpu( const GpuCtxData& gpu, double pxns, int& offset, const ImVec2& wpos, bool hover, float yMin, float yMax ) +{ + const auto w = ImGui::GetContentRegionAvail().x - 1; + const auto ty = ImGui::GetTextLineHeight(); + const auto ostep = ty + 1; + const auto nspx = 1.0 / pxns; + auto draw = ImGui::GetWindowDrawList(); + const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); + + ImGui::PushFont( m_smallFont ); + const auto sty = ImGui::GetTextLineHeight(); + const auto sstep = sty + 1; + ImGui::PopFont(); + + const auto singleThread = gpu.threadData.size() == 1; + int depth = 0; + + for( auto& td : gpu.threadData ) + { + auto& tl = td.second.timeline; + assert( !tl.empty() ); + if( tl.is_magic() ) + { + auto& tlm = *(Vector*)&tl; + if( tlm.front().GpuStart() >= 0 ) + { + const auto begin = tlm.front().GpuStart(); + const auto drift = GpuDrift( &gpu ); + if( !singleThread ) offset += sstep; + const auto partDepth = DispatchGpuZoneLevel( tl, hover, pxns, int64_t( nspx ), wpos, offset, 0, gpu.thread, yMin, yMax, begin, drift ); + if( partDepth != 0 ) + { + if( !singleThread ) + { + ImGui::PushFont( m_smallFont ); + DrawTextContrast( draw, wpos + ImVec2( ty, offset-1-sstep ), 0xFFFFAAAA, m_worker.GetThreadName( td.first ) ); + DrawLine( draw, dpos + ImVec2( 0, offset+sty-sstep ), dpos + ImVec2( w, offset+sty-sstep ), 0x22FFAAAA ); + ImGui::PopFont(); + } + + offset += ostep * partDepth; + depth += partDepth; + } + else if( !singleThread ) + { + offset -= sstep; + } + } + } + else + { + if( tl.front()->GpuStart() >= 0 ) + { + const auto begin = tl.front()->GpuStart(); + const auto drift = GpuDrift( &gpu ); + if( !singleThread ) offset += sstep; + const auto partDepth = DispatchGpuZoneLevel( tl, hover, pxns, int64_t( nspx ), wpos, offset, 0, gpu.thread, yMin, yMax, begin, drift ); + if( partDepth != 0 ) + { + if( !singleThread ) + { + ImGui::PushFont( m_smallFont ); + DrawTextContrast( draw, wpos + ImVec2( ty, offset-1-sstep ), 0xFFFFAAAA, m_worker.GetThreadName( td.first ) ); + DrawLine( draw, dpos + ImVec2( 0, offset+sty-sstep ), dpos + ImVec2( w, offset+sty-sstep ), 0x22FFAAAA ); + ImGui::PopFont(); + } + + offset += ostep * partDepth; + depth += partDepth; + } + else if( !singleThread ) + { + offset -= sstep; + } + } + } + } + return depth != 0; +} + +int View::DispatchGpuZoneLevel( const Vector>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift ) +{ + const auto ty = ImGui::GetTextLineHeight(); + const auto ostep = ty + 1; + const auto offset = _offset + ostep * depth; + + const auto yPos = wpos.y + offset; + if( yPos + ostep >= yMin && yPos <= yMax ) + { + if( vec.is_magic() ) + { + return DrawGpuZoneLevel>( *(Vector*)&vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); + } + else + { + return DrawGpuZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); + } + } + else + { + if( vec.is_magic() ) + { + return SkipGpuZoneLevel>( *(Vector*)&vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); + } + else + { + return SkipGpuZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); + } + } +} + +template +int View::DrawGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift ) +{ + const auto delay = m_worker.GetDelay(); + const auto resolution = m_worker.GetResolution(); + // cast to uint64_t, so that unended zones (end = -1) are still drawn + auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } ); + if( it == vec.end() ) return depth; + + const auto zitend = std::lower_bound( it, vec.end(), std::max( 0, m_vd.zvEnd + resolution ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuStart(), begin, drift ) < (uint64_t)r; } ); + if( it == zitend ) return depth; + + const auto w = ImGui::GetContentRegionAvail().x - 1; + const auto ty = ImGui::GetTextLineHeight(); + const auto ostep = ty + 1; + const auto offset = _offset + ostep * depth; + auto draw = ImGui::GetWindowDrawList(); + const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); + + depth++; + int maxdepth = depth; + + Adapter a; + while( it < zitend ) + { + auto& ev = a(*it); + auto end = m_worker.GetZoneEnd( ev ); + if( end == std::numeric_limits::max() ) break; + const auto start = AdjustGpuTime( ev.GpuStart(), begin, drift ); + end = AdjustGpuTime( end, begin, drift ); + const auto zsz = std::max( ( end - start ) * pxns, pxns * 0.5 ); + if( zsz < MinVisSize ) + { + const auto color = GetZoneColor( ev ); + const auto MinVisNs = MinVisSize * nspx; + int num = 0; + const auto px0 = ( start - m_vd.zvStart ) * pxns; + auto px1ns = end - m_vd.zvStart; + auto rend = end; + auto nextTime = end + MinVisNs; + for(;;) + { + const auto prevIt = it; + it = std::lower_bound( it, zitend, std::max( 0, nextTime ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } ); + if( it == prevIt ) ++it; + num += std::distance( prevIt, it ); + if( it == zitend ) break; + const auto nend = AdjustGpuTime( m_worker.GetZoneEnd( a(*it) ), begin, drift ); + const auto nsnext = nend - m_vd.zvStart; + if( nsnext < 0 || nsnext - px1ns >= MinVisNs * 2 ) break; + px1ns = nsnext; + rend = nend; + nextTime = nend + nspx; + } + const auto px1 = px1ns * pxns; + draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ), color ); + DrawZigZag( draw, wpos + ImVec2( 0, offset + ty/2 ), std::max( px0, -10.0 ), std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), ty/4, DarkenColor( color ) ); + if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty + 1 ) ) ) + { + if( num > 1 ) + { + ImGui::BeginTooltip(); + TextFocused( "Zones too small to display:", RealToString( num ) ); + ImGui::Separator(); + TextFocused( "Execution time:", TimeToString( rend - start ) ); + ImGui::EndTooltip(); + + if( IsMouseClicked( 2 ) && rend - start > 0 ) + { + ZoomToRange( start, rend ); + } + } + else + { + const auto zoneThread = thread != 0 ? thread : m_worker.DecompressThread( ev.Thread() ); + ZoneTooltip( ev ); + + if( IsMouseClicked( 2 ) && rend - start > 0 ) + { + ZoomToZone( ev ); + } + if( IsMouseClicked( 0 ) ) + { + ShowZoneInfo( ev, zoneThread ); + } + + m_gpuThread = zoneThread; + m_gpuStart = ev.CpuStart(); + m_gpuEnd = ev.CpuEnd(); + } + } + const auto tmp = RealToString( num ); + const auto tsz = ImGui::CalcTextSize( tmp ); + if( tsz.x < px1 - px0 ) + { + const auto x = px0 + ( px1 - px0 - tsz.x ) / 2; + DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFF4488DD, tmp ); + } + } + else + { + if( ev.Child() >= 0 ) + { + const auto d = DispatchGpuZoneLevel( m_worker.GetGpuChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); + if( d > maxdepth ) maxdepth = d; + } + + const char* zoneName = m_worker.GetZoneName( ev ); + auto tsz = ImGui::CalcTextSize( zoneName ); + + const auto pr0 = ( start - m_vd.zvStart ) * pxns; + const auto pr1 = ( end - m_vd.zvStart ) * pxns; + const auto px0 = std::max( pr0, -10.0 ); + const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } ); + const auto zoneColor = GetZoneColorData( ev ); + draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.color ); + if( zoneColor.highlight ) + { + draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness ); + } + else + { + const auto darkColor = DarkenColor( zoneColor.color ); + DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), zoneColor.accentColor, zoneColor.thickness ); + DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, zoneColor.thickness ); + } + if( tsz.x < zsz ) + { + const auto x = ( start - m_vd.zvStart ) * pxns + ( ( end - start ) * pxns - tsz.x ) / 2; + if( x < 0 || x > w - tsz.x ) + { + ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true ); + DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), 0xFFFFFFFF, zoneName ); + ImGui::PopClipRect(); + } + else if( ev.GpuStart() == ev.GpuEnd() ) + { + DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), 0xFFFFFFFF, zoneName ); + } + else + { + DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFFFFFFFF, zoneName ); + } + } + else + { + ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true ); + DrawTextContrast( draw, wpos + ImVec2( ( start - m_vd.zvStart ) * pxns, offset ), 0xFFFFFFFF, zoneName ); + ImGui::PopClipRect(); + } + + if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) ) + { + const auto zoneThread = thread != 0 ? thread : m_worker.DecompressThread( ev.Thread() ); + ZoneTooltip( ev ); + + if( !m_zoomAnim.active && IsMouseClicked( 2 ) ) + { + ZoomToZone( ev ); + } + if( IsMouseClicked( 0 ) ) + { + ShowZoneInfo( ev, zoneThread ); + } + + m_gpuThread = zoneThread; + m_gpuStart = ev.CpuStart(); + m_gpuEnd = ev.CpuEnd(); + } + + ++it; + } + } + return maxdepth; +} + +template +int View::SkipGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift ) +{ + const auto delay = m_worker.GetDelay(); + const auto resolution = m_worker.GetResolution(); + // cast to uint64_t, so that unended zones (end = -1) are still drawn + auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } ); + if( it == vec.end() ) return depth; + + const auto zitend = std::lower_bound( it, vec.end(), std::max( 0, m_vd.zvEnd + resolution ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuStart(), begin, drift ) < (uint64_t)r; } ); + if( it == zitend ) return depth; + + depth++; + int maxdepth = depth; + + Adapter a; + while( it < zitend ) + { + auto& ev = a(*it); + auto end = m_worker.GetZoneEnd( ev ); + if( end == std::numeric_limits::max() ) break; + const auto start = AdjustGpuTime( ev.GpuStart(), begin, drift ); + end = AdjustGpuTime( end, begin, drift ); + const auto zsz = std::max( ( end - start ) * pxns, pxns * 0.5 ); + if( zsz < MinVisSize ) + { + const auto MinVisNs = MinVisSize * nspx; + auto px1ns = end - m_vd.zvStart; + auto nextTime = end + MinVisNs; + for(;;) + { + const auto prevIt = it; + it = std::lower_bound( it, zitend, nextTime, [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } ); + if( it == prevIt ) ++it; + if( it == zitend ) break; + const auto nend = AdjustGpuTime( m_worker.GetZoneEnd( a(*it) ), begin, drift ); + const auto nsnext = nend - m_vd.zvStart; + if( nsnext - px1ns >= MinVisNs * 2 ) break; + px1ns = nsnext; + nextTime = nend + nspx; + } + } + else + { + if( ev.Child() >= 0 ) + { + const auto d = DispatchGpuZoneLevel( m_worker.GetGpuChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); + if( d > maxdepth ) maxdepth = d; + } + ++it; + } + } + return maxdepth; +} + +} diff --git a/server/TracyView_Timeline.cpp b/server/TracyView_Timeline.cpp index ca8f6079..58678e2c 100644 --- a/server/TracyView_Timeline.cpp +++ b/server/TracyView_Timeline.cpp @@ -4,6 +4,7 @@ #include "TracyMouse.hpp" #include "TracyPrint.hpp" #include "TracySourceView.hpp" +#include "TracyTimelineItemGpu.hpp" #include "TracyTimelineItemPlot.hpp" #include "TracyTimelineItemThread.hpp" #include "TracyView.hpp" @@ -339,294 +340,13 @@ void View::DrawTimeline() const auto to = 9.f; const auto th = ( ty - to ) * sqrt( 3 ) * 0.5; - // gpu zones if( m_vd.drawGpuZones ) { - for( size_t i=0; ithreadData.size() == 1; - int depth = 0; - offset += ostep; - if( showFull && !v->threadData.empty() ) - { - for( auto& td : v->threadData ) - { - auto& tl = td.second.timeline; - assert( !tl.empty() ); - if( tl.is_magic() ) - { - auto& tlm = *(Vector*)&tl; - if( tlm.front().GpuStart() >= 0 ) - { - const auto begin = tlm.front().GpuStart(); - const auto drift = GpuDrift( v ); - if( !singleThread ) offset += sstep; - const auto partDepth = DispatchGpuZoneLevel( tl, hover, pxns, int64_t( nspx ), wpos, offset, 0, v->thread, yMin, yMax, begin, drift ); - if( partDepth != 0 ) - { - if( !singleThread ) - { - ImGui::PushFont( m_smallFont ); - DrawTextContrast( draw, wpos + ImVec2( ty, offset-1-sstep ), 0xFFFFAAAA, m_worker.GetThreadName( td.first ) ); - DrawLine( draw, dpos + ImVec2( 0, offset+sty-sstep ), dpos + ImVec2( w, offset+sty-sstep ), 0x22FFAAAA ); - ImGui::PopFont(); - } - - offset += ostep * partDepth; - depth += partDepth; - } - else if( !singleThread ) - { - offset -= sstep; - } - } - } - else - { - if( tl.front()->GpuStart() >= 0 ) - { - const auto begin = tl.front()->GpuStart(); - const auto drift = GpuDrift( v ); - if( !singleThread ) offset += sstep; - const auto partDepth = DispatchGpuZoneLevel( tl, hover, pxns, int64_t( nspx ), wpos, offset, 0, v->thread, yMin, yMax, begin, drift ); - if( partDepth != 0 ) - { - if( !singleThread ) - { - ImGui::PushFont( m_smallFont ); - DrawTextContrast( draw, wpos + ImVec2( ty, offset-1-sstep ), 0xFFFFAAAA, m_worker.GetThreadName( td.first ) ); - DrawLine( draw, dpos + ImVec2( 0, offset+sty-sstep ), dpos + ImVec2( w, offset+sty-sstep ), 0x22FFAAAA ); - ImGui::PopFont(); - } - - offset += ostep * partDepth; - depth += partDepth; - } - else if( !singleThread ) - { - offset -= sstep; - } - } - } - } - } - offset += ostep * 0.2f; - - if( !m_vd.drawEmptyLabels && showFull && depth == 0 ) - { - vis.height = 0; - vis.offset = 0; - offset = oldOffset; - } - else if( yPos + ostep >= yMin && yPos <= yMax ) - { - DrawLine( draw, dpos + ImVec2( 0, oldOffset + ostep - 1 ), dpos + ImVec2( w, oldOffset + ostep - 1 ), 0x33FFFFFF ); - - if( showFull ) - { - draw->AddTriangleFilled( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( ty - to/2, oldOffset + to/2 ), wpos + ImVec2( ty * 0.5, oldOffset + to/2 + th ), 0xFFFFAAAA ); - } - else - { - draw->AddTriangle( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( to/2, oldOffset + ty - to/2 ), wpos + ImVec2( to/2 + th, oldOffset + ty * 0.5 ), 0xFF886666, 2.0f ); - } - - const bool isMultithreaded = (v->type == GpuContextType::Vulkan) || (v->type == GpuContextType::OpenCL) || (v->type == GpuContextType::Direct3D12); - - float boxwidth; - char buf[64]; - sprintf( buf, "%s context %zu", GpuContextNames[(int)v->type], i ); - if( v->name.Active() ) - { - char tmp[4096]; - sprintf( tmp, "%s: %s", buf, m_worker.GetString( v->name ) ); - DrawTextContrast( draw, wpos + ImVec2( ty, oldOffset ), showFull ? 0xFFFFAAAA : 0xFF886666, tmp ); - boxwidth = ImGui::CalcTextSize( tmp ).x; - } - else - { - DrawTextContrast( draw, wpos + ImVec2( ty, oldOffset ), showFull ? 0xFFFFAAAA : 0xFF886666, buf ); - boxwidth = ImGui::CalcTextSize( buf ).x; - } - - if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( ty + boxwidth, oldOffset + ty ) ) ) - { - if( IsMouseClicked( 0 ) ) - { - showFull = !showFull; - } - if( IsMouseClicked( 2 ) ) - { - int64_t t0 = std::numeric_limits::max(); - int64_t t1 = std::numeric_limits::min(); - for( auto& td : v->threadData ) - { - int64_t _t0; - if( td.second.timeline.is_magic() ) - { - _t0 = ((Vector*)&td.second.timeline)->front().GpuStart(); - } - else - { - _t0 = td.second.timeline.front()->GpuStart(); - } - if( _t0 >= 0 ) - { - // FIXME - t0 = std::min( t0, _t0 ); - if( td.second.timeline.is_magic() ) - { - t1 = std::max( t1, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( ((Vector*)&td.second.timeline)->back() ) ) ); - } - else - { - t1 = std::max( t1, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( *td.second.timeline.back() ) ) ); - } - } - } - if( t0 < t1 ) - { - ZoomToRange( t0, t1 ); - } - } - if( IsMouseClicked( 1 ) ) - { - ImGui::OpenPopup( "menuPopup" ); - } - - ImGui::BeginTooltip(); - ImGui::TextUnformatted( buf ); - if( v->name.Active() ) TextFocused( "Name:", m_worker.GetString( v->name ) ); - ImGui::Separator(); - if( !isMultithreaded ) - { - SmallColorBox( GetThreadColor( v->thread, 0 ) ); - ImGui::SameLine(); - TextFocused( "Thread:", m_worker.GetThreadName( v->thread ) ); - } - else - { - if( !v->threadData.empty() ) - { - if( v->threadData.size() == 1 ) - { - auto it = v->threadData.begin(); - auto tid = it->first; - if( tid == 0 ) - { - if( !it->second.timeline.empty() ) - { - if( it->second.timeline.is_magic() ) - { - auto& tl = *(Vector*)&it->second.timeline; - tid = m_worker.DecompressThread( tl.begin()->Thread() ); - } - else - { - tid = m_worker.DecompressThread( (*it->second.timeline.begin())->Thread() ); - } - } - } - SmallColorBox( GetThreadColor( tid, 0 ) ); - ImGui::SameLine(); - TextFocused( "Thread:", m_worker.GetThreadName( tid ) ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%s)", RealToString( tid ) ); - if( m_worker.IsThreadFiber( tid ) ) - { - ImGui::SameLine(); - TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" ); - } - } - else - { - ImGui::TextDisabled( "Threads:" ); - ImGui::Indent(); - for( auto& td : v->threadData ) - { - SmallColorBox( GetThreadColor( td.first, 0 ) ); - ImGui::SameLine(); - ImGui::TextUnformatted( m_worker.GetThreadName( td.first ) ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%s)", RealToString( td.first ) ); - } - ImGui::Unindent(); - } - } - } - if( !v->threadData.empty() ) - { - int64_t t0 = std::numeric_limits::max(); - for( auto& td : v->threadData ) - { - int64_t _t0; - if( td.second.timeline.is_magic() ) - { - _t0 = ((Vector*)&td.second.timeline)->front().GpuStart(); - } - else - { - _t0 = td.second.timeline.front()->GpuStart(); - } - if( _t0 >= 0 ) - { - t0 = std::min( t0, _t0 ); - } - } - if( t0 != std::numeric_limits::max() ) - { - TextFocused( "Appeared at", TimeToString( t0 ) ); - } - } - TextFocused( "Zone count:", RealToString( v->count ) ); - if( v->period != 1.f ) - { - TextFocused( "Timestamp accuracy:", TimeToString( v->period ) ); - } - if( v->overflow != 0 ) - { - ImGui::Separator(); - ImGui::TextUnformatted( "GPU timer overflow has been detected." ); - TextFocused( "Timer resolution:", RealToString( 63 - TracyLzcnt( v->overflow ) ) ); - ImGui::SameLine(); - TextDisabledUnformatted( "bits" ); - } - ImGui::EndTooltip(); - } - } - - if( ImGui::BeginPopup( "menuPopup" ) ) - { - if( ImGui::MenuItem( ICON_FA_EYE_SLASH " Hide" ) ) - { - vis.visible = false; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - - m_tc.AdjustThreadHeight( vis, oldOffset, offset ); - ImGui::PopClipRect(); - ImGui::PopID(); + m_tc.AddItem( v ); } } - - // zones if( m_vd.drawCpuData && m_worker.HasContextSwitches() ) { offset = DrawCpuData( offset, pxns, wpos, hover, yMin, yMax ); diff --git a/server/TracyView_ZoneTimeline.cpp b/server/TracyView_ZoneTimeline.cpp index 70ed998e..ec08fe99 100644 --- a/server/TracyView_ZoneTimeline.cpp +++ b/server/TracyView_ZoneTimeline.cpp @@ -888,266 +888,4 @@ int View::SkipZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, co return maxdepth; } -int View::DispatchGpuZoneLevel( const Vector>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift ) -{ - const auto ty = ImGui::GetTextLineHeight(); - const auto ostep = ty + 1; - const auto offset = _offset + ostep * depth; - - const auto yPos = wpos.y + offset; - if( yPos + ostep >= yMin && yPos <= yMax ) - { - if( vec.is_magic() ) - { - return DrawGpuZoneLevel>( *(Vector*)&vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); - } - else - { - return DrawGpuZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); - } - } - else - { - if( vec.is_magic() ) - { - return SkipGpuZoneLevel>( *(Vector*)&vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); - } - else - { - return SkipGpuZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); - } - } -} - -template -int View::DrawGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift ) -{ - const auto delay = m_worker.GetDelay(); - const auto resolution = m_worker.GetResolution(); - // cast to uint64_t, so that unended zones (end = -1) are still drawn - auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } ); - if( it == vec.end() ) return depth; - - const auto zitend = std::lower_bound( it, vec.end(), std::max( 0, m_vd.zvEnd + resolution ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuStart(), begin, drift ) < (uint64_t)r; } ); - if( it == zitend ) return depth; - - const auto w = ImGui::GetContentRegionAvail().x - 1; - const auto ty = ImGui::GetTextLineHeight(); - const auto ostep = ty + 1; - const auto offset = _offset + ostep * depth; - auto draw = ImGui::GetWindowDrawList(); - const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); - - depth++; - int maxdepth = depth; - - Adapter a; - while( it < zitend ) - { - auto& ev = a(*it); - auto end = m_worker.GetZoneEnd( ev ); - if( end == std::numeric_limits::max() ) break; - const auto start = AdjustGpuTime( ev.GpuStart(), begin, drift ); - end = AdjustGpuTime( end, begin, drift ); - const auto zsz = std::max( ( end - start ) * pxns, pxns * 0.5 ); - if( zsz < MinVisSize ) - { - const auto color = GetZoneColor( ev ); - const auto MinVisNs = MinVisSize * nspx; - int num = 0; - const auto px0 = ( start - m_vd.zvStart ) * pxns; - auto px1ns = end - m_vd.zvStart; - auto rend = end; - auto nextTime = end + MinVisNs; - for(;;) - { - const auto prevIt = it; - it = std::lower_bound( it, zitend, std::max( 0, nextTime ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } ); - if( it == prevIt ) ++it; - num += std::distance( prevIt, it ); - if( it == zitend ) break; - const auto nend = AdjustGpuTime( m_worker.GetZoneEnd( a(*it) ), begin, drift ); - const auto nsnext = nend - m_vd.zvStart; - if( nsnext < 0 || nsnext - px1ns >= MinVisNs * 2 ) break; - px1ns = nsnext; - rend = nend; - nextTime = nend + nspx; - } - const auto px1 = px1ns * pxns; - draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ), color ); - DrawZigZag( draw, wpos + ImVec2( 0, offset + ty/2 ), std::max( px0, -10.0 ), std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), ty/4, DarkenColor( color ) ); - if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty + 1 ) ) ) - { - if( num > 1 ) - { - ImGui::BeginTooltip(); - TextFocused( "Zones too small to display:", RealToString( num ) ); - ImGui::Separator(); - TextFocused( "Execution time:", TimeToString( rend - start ) ); - ImGui::EndTooltip(); - - if( IsMouseClicked( 2 ) && rend - start > 0 ) - { - ZoomToRange( start, rend ); - } - } - else - { - const auto zoneThread = thread != 0 ? thread : m_worker.DecompressThread( ev.Thread() ); - ZoneTooltip( ev ); - - if( IsMouseClicked( 2 ) && rend - start > 0 ) - { - ZoomToZone( ev ); - } - if( IsMouseClicked( 0 ) ) - { - ShowZoneInfo( ev, zoneThread ); - } - - m_gpuThread = zoneThread; - m_gpuStart = ev.CpuStart(); - m_gpuEnd = ev.CpuEnd(); - } - } - const auto tmp = RealToString( num ); - const auto tsz = ImGui::CalcTextSize( tmp ); - if( tsz.x < px1 - px0 ) - { - const auto x = px0 + ( px1 - px0 - tsz.x ) / 2; - DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFF4488DD, tmp ); - } - } - else - { - if( ev.Child() >= 0 ) - { - const auto d = DispatchGpuZoneLevel( m_worker.GetGpuChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); - if( d > maxdepth ) maxdepth = d; - } - - const char* zoneName = m_worker.GetZoneName( ev ); - auto tsz = ImGui::CalcTextSize( zoneName ); - - const auto pr0 = ( start - m_vd.zvStart ) * pxns; - const auto pr1 = ( end - m_vd.zvStart ) * pxns; - const auto px0 = std::max( pr0, -10.0 ); - const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } ); - const auto zoneColor = GetZoneColorData( ev ); - draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.color ); - if( zoneColor.highlight ) - { - draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness ); - } - else - { - const auto darkColor = DarkenColor( zoneColor.color ); - DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), zoneColor.accentColor, zoneColor.thickness ); - DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, zoneColor.thickness ); - } - if( tsz.x < zsz ) - { - const auto x = ( start - m_vd.zvStart ) * pxns + ( ( end - start ) * pxns - tsz.x ) / 2; - if( x < 0 || x > w - tsz.x ) - { - ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true ); - DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), 0xFFFFFFFF, zoneName ); - ImGui::PopClipRect(); - } - else if( ev.GpuStart() == ev.GpuEnd() ) - { - DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), 0xFFFFFFFF, zoneName ); - } - else - { - DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFFFFFFFF, zoneName ); - } - } - else - { - ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true ); - DrawTextContrast( draw, wpos + ImVec2( ( start - m_vd.zvStart ) * pxns, offset ), 0xFFFFFFFF, zoneName ); - ImGui::PopClipRect(); - } - - if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) ) - { - const auto zoneThread = thread != 0 ? thread : m_worker.DecompressThread( ev.Thread() ); - ZoneTooltip( ev ); - - if( !m_zoomAnim.active && IsMouseClicked( 2 ) ) - { - ZoomToZone( ev ); - } - if( IsMouseClicked( 0 ) ) - { - ShowZoneInfo( ev, zoneThread ); - } - - m_gpuThread = zoneThread; - m_gpuStart = ev.CpuStart(); - m_gpuEnd = ev.CpuEnd(); - } - - ++it; - } - } - return maxdepth; -} - -template -int View::SkipGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift ) -{ - const auto delay = m_worker.GetDelay(); - const auto resolution = m_worker.GetResolution(); - // cast to uint64_t, so that unended zones (end = -1) are still drawn - auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } ); - if( it == vec.end() ) return depth; - - const auto zitend = std::lower_bound( it, vec.end(), std::max( 0, m_vd.zvEnd + resolution ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuStart(), begin, drift ) < (uint64_t)r; } ); - if( it == zitend ) return depth; - - depth++; - int maxdepth = depth; - - Adapter a; - while( it < zitend ) - { - auto& ev = a(*it); - auto end = m_worker.GetZoneEnd( ev ); - if( end == std::numeric_limits::max() ) break; - const auto start = AdjustGpuTime( ev.GpuStart(), begin, drift ); - end = AdjustGpuTime( end, begin, drift ); - const auto zsz = std::max( ( end - start ) * pxns, pxns * 0.5 ); - if( zsz < MinVisSize ) - { - const auto MinVisNs = MinVisSize * nspx; - auto px1ns = end - m_vd.zvStart; - auto nextTime = end + MinVisNs; - for(;;) - { - const auto prevIt = it; - it = std::lower_bound( it, zitend, nextTime, [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } ); - if( it == prevIt ) ++it; - if( it == zitend ) break; - const auto nend = AdjustGpuTime( m_worker.GetZoneEnd( a(*it) ), begin, drift ); - const auto nsnext = nend - m_vd.zvStart; - if( nsnext - px1ns >= MinVisNs * 2 ) break; - px1ns = nsnext; - nextTime = nend + nspx; - } - } - else - { - if( ev.Child() >= 0 ) - { - const auto d = DispatchGpuZoneLevel( m_worker.GetGpuChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift ); - if( d > maxdepth ) maxdepth = d; - } - ++it; - } - } - return maxdepth; -} - }