11/*
2- * Copyright (c) 2017-2021 jMonkeyEngine
2+ * Copyright (c) 2017-2025 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
3939import java .util .*;
4040
4141/**
42- * Created by Nehon on 25/01/2017.
42+ * A detailed profiler implementation that tracks CPU and GPU times for various
43+ * application, viewport, and scene processing steps. It provides per-frame
44+ * statistics and also maintains a history for average calculations.
45+ *
46+ * @author Nehon on 25/01/2017.
4347 */
4448public class DetailedProfiler implements AppProfiler {
4549
50+ /**
51+ * The maximum number of frames to keep statistics for average calculations.
52+ */
4653 private static final int MAX_FRAMES = 100 ;
54+ /**
55+ * Stores the current frame's profiling data, mapping step paths to StatLine objects.
56+ * This map is cleared at the beginning of each frame.
57+ */
4758 private Map <String , StatLine > data ;
59+ /**
60+ * A pool of StatLine objects, mapping step paths to StatLine objects.
61+ * This pool is used to reuse StatLine instances across frames to reduce garbage collection.
62+ */
4863 private Map <String , StatLine > pool ;
64+ /**
65+ * Not currently used for timing, potentially a leftover from earlier implementation.
66+ */
4967 private long startFrame ;
68+ /**
69+ * The current frame number. Increments with each completed frame.
70+ */
5071 private static int currentFrame = 0 ;
72+ /**
73+ * Stores the path of the previously processed profiling step within the current frame.
74+ * Used to calculate the duration of the previous step.
75+ */
5176 private String prevPath = null ;
77+ /**
78+ * Flag indicating if the current frame has officially ended processing.
79+ */
5280 private boolean frameEnded = false ;
81+ /**
82+ * The renderer instance used for GPU profiling.
83+ */
5384 private Renderer renderer ;
85+ /**
86+ * Flag indicating if a GPU profiling task is currently ongoing (i.e., between startProfiling and stopProfiling).
87+ */
5488 private boolean ongoingGpuProfiling = false ;
55-
56-
89+ /**
90+ * The path component for the current application step.
91+ */
5792 private String curAppPath = null ;
93+ /**
94+ * The path component for the current viewport step.
95+ */
5896 private String curVpPath = null ;
97+ /**
98+ * The path component for the current scene processing step.
99+ */
59100 private String curSpPath = null ;
101+ /**
102+ * The last viewport step encountered. Used for path construction logic.
103+ */
60104 private VpStep lastVpStep = null ;
61-
105+ /**
106+ * StringBuilder used for constructing the full profiling path for app steps.
107+ */
62108 private final StringBuilder path = new StringBuilder (256 );
109+ /**
110+ * StringBuilder used for constructing the viewport-specific profiling path.
111+ */
63112 private final StringBuilder vpPath = new StringBuilder (256 );
64-
113+ /**
114+ * A pool of available GPU profiling task IDs.
115+ */
65116 private final Deque <Integer > idsPool = new ArrayDeque <>(100 );
66-
117+ /**
118+ * StatLine object specifically for tracking the total frame time (CPU).
119+ */
67120 StatLine frameTime ;
68121
69122
123+ /**
124+ * Records the time taken for various application-level steps.
125+ * This method is called at predefined points in the application lifecycle.
126+ *
127+ * @param step The application step being profiled.
128+ */
70129 @ Override
71130 public void appStep (AppStep step ) {
72131
@@ -115,24 +174,30 @@ public void appStep(AppStep step) {
115174 }
116175 }
117176 if (step == AppStep .EndFrame ) {
118-
119177 closeFrame ();
120178 }
121179 }
122-
123-
180+
181+ /**
182+ * Records the time taken for application sub-steps, providing additional hierarchical detail.
183+ *
184+ * @param additionalInfo Optional strings to further qualify the sub-step path.
185+ */
124186 @ Override
125187 public void appSubStep (String ... additionalInfo ) {
126188 if (data != null ) {
127- String pathStep = getPath ("" , additionalInfo );
128189 path .setLength (0 );
129- path .append (curAppPath ).append (pathStep );
190+ path .append (curAppPath );
191+ appendSubPath (path , additionalInfo ); // Reusing helper for sub-path append
130192 addStep (path .toString (), System .nanoTime ());
131193 }
132194 }
133195
196+ /**
197+ * Completes the profiling for the current frame.
198+ * This involves stopping any ongoing GPU profiling and closing all active StatLine entries.
199+ */
134200 private void closeFrame () {
135- //close frame
136201 if (data != null ) {
137202 if (ongoingGpuProfiling && renderer != null ) {
138203 renderer .stopProfiling ();
@@ -147,24 +212,37 @@ private void closeFrame() {
147212 }
148213 }
149214
215+ /**
216+ * Records the time taken for viewport-specific steps, including rendering buckets.
217+ *
218+ * @param step The viewport step being profiled.
219+ * @param vp The ViewPort associated with this step.
220+ * @param bucket The render queue bucket, if applicable (null for non-bucket steps).
221+ */
150222 @ Override
151223 public void vpStep (VpStep step , ViewPort vp , RenderQueue .Bucket bucket ) {
152224
153225 if (data != null ) {
154226 vpPath .setLength (0 );
155- vpPath .append (vp .getName ()).append ("/" )
156- .append ((bucket == null ? step .name () : bucket .name () + " Bucket" ));
227+ vpPath .append (vp .getName ()).append ("/" );
228+ if (bucket == null ) {
229+ vpPath .append (step .name ());
230+ } else {
231+ vpPath .append (bucket .name ()).append (" Bucket" );
232+ }
233+
157234 path .setLength (0 );
235+ // Optimized path construction
236+ path .append (curAppPath ).append ("/" );
237+
158238 if ((lastVpStep == VpStep .PostQueue || lastVpStep == VpStep .PostFrame ) && bucket != null ) {
159- path .append (curAppPath ).append ("/" ).append (curVpPath ).append (curSpPath ).append ("/" )
160- .append (vpPath );
239+ path .append (curVpPath ).append (curSpPath ).append ("/" ).append (vpPath );
161240 curVpPath = vpPath .toString ();
162241 } else {
163242 if (bucket != null ) {
164- path .append (curAppPath ).append ("/" ).append (curVpPath ).append ("/" )
165- .append (bucket .name () + " Bucket" );
243+ path .append (curVpPath ).append ("/" ).append (bucket .name ()).append (" Bucket" );
166244 } else {
167- path .append (curAppPath ). append ( "/" ). append ( vpPath );
245+ path .append (vpPath );
168246 curVpPath = vpPath .toString ();
169247 }
170248 }
@@ -174,30 +252,49 @@ public void vpStep(VpStep step, ViewPort vp, RenderQueue.Bucket bucket) {
174252 }
175253 }
176254
255+ /**
256+ * Records the time taken for scene processing steps, allowing for additional detail.
257+ *
258+ * @param step The scene processing step being profiled.
259+ * @param additionalInfo Optional strings to further qualify the step path.
260+ */
177261 @ Override
178262 public void spStep (SpStep step , String ... additionalInfo ) {
179-
180263 if (data != null ) {
181264 curSpPath = getPath ("" , additionalInfo );
182265 path .setLength (0 );
183266 path .append (curAppPath ).append ("/" ).append (curVpPath ).append (curSpPath );
184267 addStep (path .toString (), System .nanoTime ());
185268 }
186-
187269 }
188270
271+ /**
272+ * Returns a map of the collected statistics for the current frame.
273+ * The keys are the hierarchical paths of the profiled steps, and the values
274+ * are {@link StatLine} objects containing CPU and GPU time data.
275+ *
276+ * @return A Map of StatLine objects, or null if profiling has not started.
277+ */
189278 public Map <String , StatLine > getStats () {
190- if (data != null ) {
191- return data ; //new LinkedHashMap<>(data);
192- }
193- return null ;
279+ return data ;
194280 }
195281
282+ /**
283+ * Calculates the average CPU time for the entire frame.
284+ *
285+ * @return The average frame CPU time in nanoseconds.
286+ */
196287 public double getAverageFrameTime () {
197288 return frameTime .getAverageCpu ();
198289 }
199290
200-
291+ /**
292+ * Adds a new profiling step or updates an existing one with its start time.
293+ * Handles stopping the previous GPU profiling task and starting a new one.
294+ *
295+ * @param path The hierarchical path of the profiling step.
296+ * @param value The system nano time when this step began.
297+ */
201298 private void addStep (String path , long value ) {
202299 if (ongoingGpuProfiling && renderer != null ) {
203300 renderer .stopProfiling ();
@@ -225,26 +322,36 @@ private void addStep(String path, long value) {
225322 }
226323 ongoingGpuProfiling = true ;
227324 prevPath = path ;
228-
229325 }
230326
231327 private String getPath (String step , String ... subPath ) {
232- StringBuilder path = new StringBuilder (step );
328+ StringBuilder sb = new StringBuilder (step );
329+ appendSubPath (sb , subPath );
330+ return sb .toString ();
331+ }
332+
333+ private void appendSubPath (StringBuilder pathBuilder , String ... subPath ) {
233334 if (subPath != null ) {
234335 for (String s : subPath ) {
235- path .append ("/" ).append (s );
336+ pathBuilder .append ("/" ).append (s );
236337 }
237338 }
238- return path .toString ();
239339 }
240340
341+ /**
342+ * Sets the {@link Renderer} instance to be used for GPU profiling.
343+ * This method should be called once the renderer is available.
344+ *
345+ * @param renderer The renderer instance.
346+ */
241347 public void setRenderer (Renderer renderer ) {
242348 this .renderer = renderer ;
349+ // Initialize the pool of GPU profiling task IDs
243350 poolTaskIds (renderer );
244351 }
245352
246353 private void poolTaskIds (Renderer renderer ) {
247- int [] ids = renderer .generateProfilingTasks (100 );
354+ int [] ids = renderer .generateProfilingTasks (MAX_FRAMES );
248355 for (int id : ids ) {
249356 idsPool .push (id );
250357 }
@@ -258,7 +365,12 @@ private int getUnusedTaskId() {
258365 return idsPool .pop ();
259366 }
260367
368+ /**
369+ * Represents a single line of statistics for a profiled step,
370+ * tracking CPU and GPU times over a set number of frames.
371+ */
261372 public static class StatLine {
373+
262374 private final long [] cpuTimes = new long [MAX_FRAMES ];
263375 private final long [] gpuTimes = new long [MAX_FRAMES ];
264376 private int startCursor = 0 ;
0 commit comments