aboutsummaryrefslogtreecommitdiff
path: root/src/zen/trace/timeline_query.cpp
blob: d90c79a296cd5f3d5e9c1409680ca5a3be0f29c2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright Epic Games, Inc. All Rights Reserved.

#include "timeline_query.h"

#include <algorithm>

namespace zen::trace_detail {

namespace {

	// Pick the LOD level a given resolution should read from. Mirrors the
	// historical selection in trace_viewer_service.cpp: resolution 0 reads the
	// raw LOD 0; otherwise the smallest LOD whose ResolutionUs >= the request
	// wins, falling back to the coarsest level if none qualify.
	//
	// Returned values: 0 == raw scopes (LOD 0), 1..kTimelineLodCount == DetailLevels[lod-1].
	size_t SelectLodIndex(uint32_t ResolutionUs)
	{
		if (ResolutionUs == 0)
		{
			return 0;
		}
		for (size_t I = 0; I < kTimelineLodCount; ++I)
		{
			if (kTimelineLodResolutions[I] >= ResolutionUs)
			{
				return I + 1;
			}
		}
		return kTimelineLodCount;
	}

	const eastl::vector<TimelineScope>& LodScopes(const ThreadTimeline& Timeline, size_t LodIndex)
	{
		if (LodIndex == 0)
		{
			return Timeline.Scopes;
		}
		return Timeline.DetailLevels[LodIndex - 1].Scopes;
	}

	void ExtractScopesInto(const ThreadTimeline& Timeline, const TimelineQueryRequest& Req, std::vector<TimelineScopeView>& Out)
	{
		const eastl::vector<TimelineScope>& Scopes = LodScopes(Timeline, SelectLodIndex(Req.ResolutionUs));

		auto MidIt =
			std::lower_bound(Scopes.begin(), Scopes.end(), Req.StartUs, [](const TimelineScope& S, uint32_t V) { return S.BeginUs < V; });

		for (auto It = Scopes.begin(); It != MidIt; ++It)
		{
			if ((It->BeginUs + It->DurationUs) < Req.StartUs || It->DurationUs < Req.MinDurUs)
			{
				continue;
			}
			Out.push_back({It->BeginUs, It->DurationUs, It->NameId, It->Depth, It->MergeCount});
		}
		for (auto It = MidIt; It != Scopes.end(); ++It)
		{
			if (It->BeginUs > Req.EndUs)
			{
				break;
			}
			if (It->DurationUs < Req.MinDurUs)
			{
				continue;
			}
			Out.push_back({It->BeginUs, It->DurationUs, It->NameId, It->Depth, It->MergeCount});
		}
	}

	const ThreadTimeline* FindThread(const TraceModel& Model, uint32_t ThreadId)
	{
		auto It = std::find_if(Model.Timelines.begin(), Model.Timelines.end(), [ThreadId](const ThreadTimeline& T) {
			return T.ThreadId == ThreadId;
		});
		return (It != Model.Timelines.end()) ? &*It : nullptr;
	}

	class InMemoryTimelineQuery final : public TimelineQuery
	{
	public:
		explicit InMemoryTimelineQuery(const TraceModel& Model) : m_Model(Model) {}

		void QueryThread(uint32_t ThreadId, const TimelineQueryRequest& Req, std::vector<TimelineScopeView>& Out) const override
		{
			const ThreadTimeline* Timeline = FindThread(m_Model, ThreadId);
			if (Timeline)
			{
				ExtractScopesInto(*Timeline, Req, Out);
			}
		}

		void QueryBatch(std::span<const uint32_t> ThreadIds, const TimelineQueryRequest& Req, BatchResult& Out) const override
		{
			Out.Scopes.clear();
			Out.Ranges.clear();
			Out.Ranges.reserve(ThreadIds.size());

			for (uint32_t ThreadId : ThreadIds)
			{
				const uint32_t		  Begin	   = uint32_t(Out.Scopes.size());
				const ThreadTimeline* Timeline = FindThread(m_Model, ThreadId);
				if (Timeline)
				{
					ExtractScopesInto(*Timeline, Req, Out.Scopes);
				}
				Out.Ranges.push_back({Begin, uint32_t(Out.Scopes.size())});
			}
		}

	private:
		const TraceModel& m_Model;
	};

}  // namespace

std::unique_ptr<TimelineQuery>
MakeInMemoryTimelineQuery(const TraceModel& Model)
{
	return std::make_unique<InMemoryTimelineQuery>(Model);
}

}  // namespace zen::trace_detail