Skip to content

Commit 2402835

Browse files
committed
Add PAGX profiler frame histogram and node distribution panel.
1 parent 349d1a1 commit 2402835

3 files changed

Lines changed: 156 additions & 129 deletions

File tree

viewer/assets/qml/Profiler.qml

Lines changed: 127 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,133 @@ Item {
217217
}
218218
}
219219

220+
/* Spacer before Node Statistics (only visible in PAGX mode) */
221+
Item {
222+
width: 1
223+
height: sectionSpacing
224+
visible: nodeStatistics.visible
225+
}
226+
227+
/* Node Statistics for PAGX */
228+
Rectangle {
229+
id: nodeStatistics
230+
visible: runTimeDataModel && runTimeDataModel.nodeStatsModel.totalCount > 0
231+
height: visible ? nodeStatsContent.height : 0
232+
width: parent.width
233+
color: "#00000000"
234+
235+
Column {
236+
id: nodeStatsContent
237+
width: parent.width
238+
spacing: 1
239+
240+
// Title row
241+
Rectangle {
242+
height: 24
243+
width: parent.width
244+
color: "#2D2D37"
245+
Text {
246+
text: qsTr("Node Distribution")
247+
font.pixelSize: 11
248+
font.bold: true
249+
anchors.verticalCenter: parent.verticalCenter
250+
anchors.left: parent.left
251+
anchors.leftMargin: 8
252+
color: "#FFFFFF"
253+
renderType: Text.NativeRendering
254+
}
255+
Text {
256+
text: runTimeDataModel && runTimeDataModel.nodeStatsModel ? runTimeDataModel.nodeStatsModel.totalCount + " " + qsTr("nodes") : ""
257+
font.pixelSize: 11
258+
anchors.verticalCenter: parent.verticalCenter
259+
anchors.right: parent.right
260+
anchors.rightMargin: 8
261+
color: "#9B9B9B"
262+
renderType: Text.NativeRendering
263+
}
264+
}
265+
266+
// Stacked bar showing distribution
267+
Rectangle {
268+
height: 20
269+
width: parent.width
270+
color: "#2D2D37"
271+
272+
Row {
273+
id: nodeBarRow
274+
anchors.fill: parent
275+
anchors.margins: 4
276+
spacing: 1
277+
278+
Repeater {
279+
model: runTimeDataModel ? runTimeDataModel.nodeStatsModel : null
280+
Rectangle {
281+
height: parent.height
282+
width: {
283+
var total = runTimeDataModel && runTimeDataModel.nodeStatsModel ? Math.max(runTimeDataModel.nodeStatsModel.totalCount, 1) : 1;
284+
return Math.max((nodeBarRow.width - 10) * count / total, 0);
285+
}
286+
color: colorCode
287+
radius: 2
288+
}
289+
}
290+
}
291+
}
292+
293+
// Detail rows
294+
Repeater {
295+
model: runTimeDataModel ? runTimeDataModel.nodeStatsModel : null
296+
Rectangle {
297+
height: 20
298+
width: nodeStatsContent.width
299+
color: "#2D2D37"
300+
301+
Rectangle {
302+
width: 4
303+
height: 12
304+
radius: 2
305+
color: colorCode
306+
anchors.verticalCenter: parent.verticalCenter
307+
anchors.left: parent.left
308+
anchors.leftMargin: 8
309+
}
310+
311+
Text {
312+
text: name
313+
font.pixelSize: 10
314+
anchors.verticalCenter: parent.verticalCenter
315+
anchors.left: parent.left
316+
anchors.leftMargin: 18
317+
color: "#9B9B9B"
318+
renderType: Text.NativeRendering
319+
}
320+
321+
Text {
322+
text: {
323+
var total = runTimeDataModel && runTimeDataModel.nodeStatsModel ? Math.max(runTimeDataModel.nodeStatsModel.totalCount, 1) : 1;
324+
return Math.round(count * 100 / total) + "%";
325+
}
326+
font.pixelSize: 10
327+
anchors.verticalCenter: parent.verticalCenter
328+
anchors.horizontalCenter: parent.horizontalCenter
329+
color: "#9B9B9B"
330+
renderType: Text.NativeRendering
331+
}
332+
333+
Text {
334+
text: count
335+
font.pixelSize: 11
336+
anchors.verticalCenter: parent.verticalCenter
337+
anchors.right: parent.right
338+
anchors.rightMargin: 8
339+
color: "#FFFFFF"
340+
renderType: Text.NativeRendering
341+
}
342+
}
343+
}
344+
}
345+
}
346+
220347
/* Spacer before Performance Chart (only visible in PAG mode) */
221348
Item {
222349
width: 1
@@ -663,133 +790,6 @@ Item {
663790
}
664791
}
665792

666-
/* Spacer before Node Statistics (only visible in PAGX mode) */
667-
Item {
668-
width: 1
669-
height: sectionSpacing
670-
visible: nodeStatistics.visible
671-
}
672-
673-
/* Node Statistics for PAGX */
674-
Rectangle {
675-
id: nodeStatistics
676-
visible: runTimeDataModel && runTimeDataModel.nodeStatsModel.totalCount > 0
677-
height: visible ? nodeStatsContent.height : 0
678-
width: parent.width
679-
color: "#00000000"
680-
681-
Column {
682-
id: nodeStatsContent
683-
width: parent.width
684-
spacing: 1
685-
686-
// Title row
687-
Rectangle {
688-
height: 24
689-
width: parent.width
690-
color: "#2D2D37"
691-
Text {
692-
text: qsTr("Node Distribution")
693-
font.pixelSize: 11
694-
font.bold: true
695-
anchors.verticalCenter: parent.verticalCenter
696-
anchors.left: parent.left
697-
anchors.leftMargin: 8
698-
color: "#FFFFFF"
699-
renderType: Text.NativeRendering
700-
}
701-
Text {
702-
text: runTimeDataModel && runTimeDataModel.nodeStatsModel ? runTimeDataModel.nodeStatsModel.totalCount + " " + qsTr("nodes") : ""
703-
font.pixelSize: 11
704-
anchors.verticalCenter: parent.verticalCenter
705-
anchors.right: parent.right
706-
anchors.rightMargin: 8
707-
color: "#9B9B9B"
708-
renderType: Text.NativeRendering
709-
}
710-
}
711-
712-
// Stacked bar showing distribution
713-
Rectangle {
714-
height: 20
715-
width: parent.width
716-
color: "#2D2D37"
717-
718-
Row {
719-
id: nodeBarRow
720-
anchors.fill: parent
721-
anchors.margins: 4
722-
spacing: 1
723-
724-
Repeater {
725-
model: runTimeDataModel ? runTimeDataModel.nodeStatsModel : null
726-
Rectangle {
727-
height: parent.height
728-
width: {
729-
var total = runTimeDataModel && runTimeDataModel.nodeStatsModel ? Math.max(runTimeDataModel.nodeStatsModel.totalCount, 1) : 1;
730-
return Math.max((nodeBarRow.width - 10) * count / total, 0);
731-
}
732-
color: colorCode
733-
radius: 2
734-
}
735-
}
736-
}
737-
}
738-
739-
// Detail rows
740-
Repeater {
741-
model: runTimeDataModel ? runTimeDataModel.nodeStatsModel : null
742-
Rectangle {
743-
height: 20
744-
width: nodeStatsContent.width
745-
color: "#2D2D37"
746-
747-
Rectangle {
748-
width: 4
749-
height: 12
750-
radius: 2
751-
color: colorCode
752-
anchors.verticalCenter: parent.verticalCenter
753-
anchors.left: parent.left
754-
anchors.leftMargin: 8
755-
}
756-
757-
Text {
758-
text: name
759-
font.pixelSize: 10
760-
anchors.verticalCenter: parent.verticalCenter
761-
anchors.left: parent.left
762-
anchors.leftMargin: 18
763-
color: "#9B9B9B"
764-
renderType: Text.NativeRendering
765-
}
766-
767-
Text {
768-
text: {
769-
var total = runTimeDataModel && runTimeDataModel.nodeStatsModel ? Math.max(runTimeDataModel.nodeStatsModel.totalCount, 1) : 1;
770-
return Math.round(count * 100 / total) + "%";
771-
}
772-
font.pixelSize: 10
773-
anchors.verticalCenter: parent.verticalCenter
774-
anchors.horizontalCenter: parent.horizontalCenter
775-
color: "#9B9B9B"
776-
renderType: Text.NativeRendering
777-
}
778-
779-
Text {
780-
text: count
781-
font.pixelSize: 11
782-
anchors.verticalCenter: parent.verticalCenter
783-
anchors.right: parent.right
784-
anchors.rightMargin: 8
785-
color: "#FFFFFF"
786-
renderType: Text.NativeRendering
787-
}
788-
}
789-
}
790-
}
791-
}
792-
793793
/* Spacer before FPS Info (only visible in PAGX mode) */
794794
Item {
795795
width: 1

viewer/src/profiling/PAGRunTimeDataModel.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,17 @@ void PAGRunTimeDataModel::setPAGFile(std::shared_ptr<PAGFile> pagFile) {
128128
}
129129

130130
void PAGRunTimeDataModel::setPAGXDocument(std::shared_ptr<pagx::PAGXDocument> pagxDocument) {
131-
frameModeEnabled = false;
132-
totalFrame = pagxDocument ? 1 : 0;
131+
if (pagxDocument != nullptr && !pagxDocument->animations.empty()) {
132+
auto* anim = pagxDocument->animations[0];
133+
totalFrame = static_cast<int64_t>(anim->duration);
134+
if (totalFrame < 1) {
135+
totalFrame = 1;
136+
}
137+
frameModeEnabled = true;
138+
} else {
139+
totalFrame = pagxDocument ? 1 : 0;
140+
frameModeEnabled = false;
141+
}
133142
currentFrame = pagxDocument ? 0 : -1;
134143
lastUpdatedFrame = -1;
135144
chartDataModel.clearItems();

viewer/src/rendering/pagx/PAGXRenderer.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,24 @@ IContentRenderer::RenderMetrics PAGXRenderer::flush() {
161161
}
162162
}
163163

164+
// Compute current frame for the profiling histogram.
165+
if (state.timeline != nullptr && state.timeline->duration() > 0) {
166+
auto rate = state.timeline->frameRate();
167+
if (rate > 0) {
168+
auto durationUs = state.timeline->duration();
169+
auto currentUs = state.timeline->currentTime();
170+
auto totalFrames =
171+
static_cast<int64_t>(std::round(static_cast<double>(durationUs) * rate / 1000000.0));
172+
auto frame = static_cast<int64_t>(static_cast<double>(currentUs) * rate / 1000000.0);
173+
// Clamp to the last valid frame index so an animation ending exactly on a frame boundary
174+
// (integer fps) still records the final frame instead of overflowing totalFrame.
175+
if (totalFrames > 0 && frame >= totalFrames) {
176+
frame = totalFrames - 1;
177+
}
178+
metrics.currentFrame = frame;
179+
}
180+
}
181+
164182
// Apply fit-to-screen transform via scene display options.
165183
if (state.contentWidth > 0 && state.contentHeight > 0) {
166184
int surfaceWidth = tgfxSurface->width();

0 commit comments

Comments
 (0)