载入中...
搜索中...
未找到
ProfilerModel.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <map>
6#include <tuple>
7#include <utility>
8
10namespace {
11
12bool finiteNonNegative(double value) { return std::isfinite(value) && value >= 0.0; }
13
14bool contains(const std::string& value, const std::string& filter) {
15 return filter.empty() || value.find(filter) != std::string::npos;
16}
17
18} // namespace
19
21
23 if (!finiteNonNegative(budgets.cpuFrameMs) || !finiteNonNegative(budgets.gpuFrameMs) ||
24 !finiteNonNegative(budgets.zoneSelfMs) || budgets.historyFrames < 2 ||
25 budgets.historyFrames > 36000) {
26 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.profiler.invalid-budgets"),
27 "Profiler budgets or history capacity are invalid");
28 }
29 budgets_ = budgets;
30 while (history_.size() > budgets_.historyFrames) history_.pop_front();
31 return eve::editing::applied<void>();
32}
33
35 if (frame.sequence == 0 || !finiteNonNegative(frame.cpuFrameMs) ||
36 !finiteNonNegative(frame.gpuFrameMs) || frame.zones.size() > 100000) {
37 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.profiler.invalid-frame"),
38 "Profiler frame shape or timing is invalid");
39 }
40 if (!history_.empty() && frame.sequence <= history_.back().sequence) {
41 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId("editor.profiler.stale-frame"),
42 "Profiler frame sequence is stale");
43 }
44 for (const auto& zone : frame.zones) {
45 if (zone.module.empty() || zone.name.empty() || zone.thread.empty() ||
46 !finiteNonNegative(zone.selfMs) || !finiteNonNegative(zone.totalMs) ||
47 zone.selfMs > zone.totalMs || zone.count < 1 || zone.depth < 0) {
48 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.profiler.invalid-zone"),
49 "Profiler zone identity or timing is invalid");
50 }
51 }
52 history_.push_back(std::move(frame));
53 while (history_.size() > budgets_.historyFrames) history_.pop_front();
54 return eve::editing::applied<void>();
55}
56
57void EditorProfilerModel::clear() { history_.clear(); }
58
59std::vector<EditorProfilerZone> EditorProfilerModel::query(
60 const EditorProfilerQuery& options) const {
61 std::vector<EditorProfilerZone> result;
62 if (history_.empty() || options.limit == 0) return result;
63 for (const auto& zone : history_.back().zones) {
64 if (contains(zone.module, options.module) && contains(zone.thread, options.thread) &&
65 (contains(zone.name, options.text) || contains(zone.module, options.text))) {
66 result.push_back(zone);
67 }
68 }
69 const auto less = [&](const EditorProfilerZone& left, const EditorProfilerZone& right) {
70 switch (options.sort) {
71 case EditorProfilerSort::SelfTime: return left.selfMs < right.selfMs;
72 case EditorProfilerSort::TotalTime: return left.totalMs < right.totalMs;
73 case EditorProfilerSort::Count: return left.count < right.count;
75 return std::tie(left.module, left.name, left.thread) <
76 std::tie(right.module, right.name, right.thread);
77 }
78 return false;
79 };
80 std::stable_sort(result.begin(), result.end(), [&](const auto& left, const auto& right) {
81 return options.descending ? less(right, left) : less(left, right);
82 });
83 if (result.size() > options.limit) result.resize(options.limit);
84 return result;
85}
86
87std::vector<EditorProfilerModuleSummary> EditorProfilerModel::moduleSummary() const {
88 std::map<std::string, EditorProfilerModuleSummary> grouped;
89 if (!history_.empty()) {
90 for (const auto& zone : history_.back().zones) {
91 auto& summary = grouped[zone.module];
92 summary.module = zone.module;
93 summary.selfMs += zone.selfMs;
94 summary.totalMs += zone.totalMs;
95 summary.count += zone.count;
96 ++summary.zones;
97 }
98 }
99 std::vector<EditorProfilerModuleSummary> result;
100 for (auto& [name, summary] : grouped) result.push_back(std::move(summary));
101 std::stable_sort(result.begin(), result.end(), [](const auto& left, const auto& right) {
102 if (left.selfMs != right.selfMs) return left.selfMs > right.selfMs;
103 return left.module < right.module;
104 });
105 return result;
106}
107
108std::vector<EditorDiagnostic> EditorProfilerModel::diagnostics() const {
109 std::vector<EditorDiagnostic> result;
110 if (history_.empty()) return result;
111 const auto& frame = history_.back();
112 if (budgets_.cpuFrameMs > 0.0 && frame.cpuFrameMs > budgets_.cpuFrameMs) {
113 result.push_back(eve::editing::ruleDiagnostic(
114 eve::DiagnosticCode::PreconditionViolation, RuleId("editor.profiler.cpu-budget"),
115 DiagnosticSeverity::Warning, "CPU frame time exceeds the editor budget"));
116 }
117 if (frame.gpuTimingAvailable && budgets_.gpuFrameMs > 0.0 &&
118 frame.gpuFrameMs > budgets_.gpuFrameMs) {
119 result.push_back(eve::editing::ruleDiagnostic(
120 eve::DiagnosticCode::PreconditionViolation, RuleId("editor.profiler.gpu-budget"),
121 DiagnosticSeverity::Warning, "GPU frame time exceeds the editor budget"));
122 }
123 if (budgets_.zoneSelfMs > 0.0) {
124 for (const auto& zone : frame.zones) {
125 if (zone.selfMs > budgets_.zoneSelfMs) {
126 result.push_back(eve::editing::ruleDiagnostic(
128 RuleId("editor.profiler.zone-budget"), DiagnosticSeverity::Warning,
129 "Profiler zone exceeds its self-time budget: " + zone.module + "/" +
130 zone.name));
131 }
132 }
133 }
134 return result;
135}
136
137} // namespace eve::profiler_editing
double value
HexVec3 left
HexVec3 right
std::string name
std::string filter
const SquirrelValueOptions & options
Move-only operation result carrying either a value or Status.
Definition Result.h:155
EditorProfilerModel()
Construct with default budgets and a 300-frame history.
Result< void > configure(EditorProfilerBudgets budgets)
Validate and atomically replace editor-only performance budgets.
EditorProfilerBudgets budgets() const
Return the current editor-only budgets by value.
std::vector< EditorProfilerZone > query(const EditorProfilerQuery &query) const
Query and sort copied hotspot rows from the latest frame.
std::vector< EditorDiagnostic > diagnostics() const
Produce CPU/GPU/zone budget diagnostics for the latest frame.
void clear()
Clear retained frames without changing configured budgets.
Result< void > ingest(EditorProfilerFrame frame)
Append an owning completed-frame snapshot.
std::vector< EditorProfilerModuleSummary > moduleSummary() const
Aggregate copied rows by module for the latest frame.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
editing::RuleId RuleId
Configurable performance budgets used only for editor diagnostics.
One owning completed-frame sample retained by the editor.
std::vector< EditorProfilerZone > zones
Non-persistent query applied to the latest profiler frame.
One owning profiler hotspot row projected for editor presentation.