From a5171305282c8589065751a9aed95b94e346f4ac Mon Sep 17 00:00:00 2001 From: pspeed42 Date: Thu, 11 Sep 2014 16:20:08 -0400 Subject: [PATCH] Added hooks for detailed performance profiling. Implementors can provide their own AppProfiler implementation to collect and/or visualize timing stats however they want, even at the viewport level if they choose. A basic profiler implementation will be following shortly that does simple update vs render frame timings. --- .../main/java/com/jme3/app/Application.java | 31 ++++++++++++++++++ .../java/com/jme3/app/SimpleApplication.java | 16 ++++++++-- .../java/com/jme3/renderer/RenderManager.java | 32 +++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/app/Application.java b/jme3-core/src/main/java/com/jme3/app/Application.java index 396b3ecb9..c18e5f103 100644 --- a/jme3-core/src/main/java/com/jme3/app/Application.java +++ b/jme3-core/src/main/java/com/jme3/app/Application.java @@ -38,6 +38,8 @@ import com.jme3.audio.AudioRenderer; import com.jme3.audio.Listener; import com.jme3.input.*; import com.jme3.math.Vector3f; +import com.jme3.profile.AppProfiler; +import com.jme3.profile.AppStep; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; @@ -91,6 +93,8 @@ public class Application implements SystemListener { protected InputManager inputManager; protected AppStateManager stateManager; + protected AppProfiler prof; + private final ConcurrentLinkedQueue> taskQueue = new ConcurrentLinkedQueue>(); /** @@ -250,6 +254,11 @@ public class Application implements SystemListener { renderManager = new RenderManager(renderer); //Remy - 09/14/2010 setted the timer in the renderManager renderManager.setTimer(timer); + + if (prof != null) { + renderManager.setAppProfiler(prof); + } + viewPort = renderManager.createMainView("Default", cam); viewPort.setClearFlags(true, true, true); @@ -387,6 +396,25 @@ public class Application implements SystemListener { context.create(false); } + /** + * Sets an AppProfiler hook that will be called back for + * specific steps within a single update frame. Value defaults + * to null. + */ + public void setAppProfiler(AppProfiler prof) { + this.prof = prof; + if (renderManager != null) { + renderManager.setAppProfiler(prof); + } + } + + /** + * Returns the current AppProfiler hook, or null if none is set. + */ + public AppProfiler getAppProfiler() { + return prof; + } + /** * Initializes the application's canvas for use. *

@@ -595,6 +623,7 @@ public class Application implements SystemListener { // Make sure the audio renderer is available to callables AudioContext.setAudioRenderer(audioRenderer); + if (prof!=null) prof.appStep(AppStep.QueuedTasks); runQueuedTasks(); if (speed == 0 || paused) @@ -603,10 +632,12 @@ public class Application implements SystemListener { timer.update(); if (inputEnabled){ + if (prof!=null) prof.appStep(AppStep.ProcessInput); inputManager.update(timer.getTimePerFrame()); } if (audioRenderer != null){ + if (prof!=null) prof.appStep(AppStep.ProcessAudio); audioRenderer.update(timer.getTimePerFrame()); } diff --git a/jme3-core/src/main/java/com/jme3/app/SimpleApplication.java b/jme3-core/src/main/java/com/jme3/app/SimpleApplication.java index 3a746b32c..9433754dc 100644 --- a/jme3-core/src/main/java/com/jme3/app/SimpleApplication.java +++ b/jme3-core/src/main/java/com/jme3/app/SimpleApplication.java @@ -38,6 +38,7 @@ import com.jme3.input.FlyByCamera; import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.KeyTrigger; +import com.jme3.profile.AppStep; import com.jme3.renderer.RenderManager; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.Node; @@ -228,30 +229,39 @@ public abstract class SimpleApplication extends Application { @Override public void update() { + if (prof!=null) prof.appStep(AppStep.BeginFrame); + super.update(); // makes sure to execute AppTasks if (speed == 0 || paused) { return; } float tpf = timer.getTimePerFrame() * speed; - + // update states + if (prof!=null) prof.appStep(AppStep.StateManagerUpdate); stateManager.update(tpf); // simple update and root node simpleUpdate(tpf); + if (prof!=null) prof.appStep(AppStep.SpatialUpdate); rootNode.updateLogicalState(tpf); guiNode.updateLogicalState(tpf); rootNode.updateGeometricState(); guiNode.updateGeometricState(); - + // render states + if (prof!=null) prof.appStep(AppStep.StateManagerRender); stateManager.render(renderManager); + + if (prof!=null) prof.appStep(AppStep.RenderFrame); renderManager.render(tpf, context.isRenderable()); simpleRender(renderManager); - stateManager.postRender(); + stateManager.postRender(); + + if (prof!=null) prof.appStep(AppStep.EndFrame); } public void setDisplayFps(boolean show) { diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index 1dfc6e0a3..95bcf0cf2 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -37,6 +37,9 @@ import com.jme3.material.RenderState; import com.jme3.material.Technique; import com.jme3.math.*; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; +import com.jme3.profile.AppStep; +import com.jme3.profile.VpStep; import com.jme3.renderer.queue.GeometryList; import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue.Bucket; @@ -80,6 +83,7 @@ public class RenderManager { private Matrix4f orthoMatrix = new Matrix4f(); private String tmpTech; private boolean handleTranlucentBucket = true; + private AppProfiler prof; /** * Create a high-level rendering interface over the @@ -377,6 +381,15 @@ public class RenderManager { uniformBindingManager.setTimer(timer); } + /** + * Sets an AppProfiler hook that will be called back for + * specific steps within a single update frame. Value defaults + * to null. + */ + public void setAppProfiler(AppProfiler prof) { + this.prof = prof; + } + /** * Returns the forced technique name set. * @@ -779,10 +792,12 @@ public class RenderManager { // render opaque objects with default depth range // opaque objects are sorted front-to-back, reducing overdraw + if (prof!=null) prof.vpStep(VpStep.RenderBucket, vp, Bucket.Opaque); rq.renderQueue(Bucket.Opaque, this, cam, flush); // render the sky, with depth range set to the farthest if (!rq.isQueueEmpty(Bucket.Sky)) { + if (prof!=null) prof.vpStep(VpStep.RenderBucket, vp, Bucket.Sky); renderer.setDepthRange(1, 1); rq.renderQueue(Bucket.Sky, this, cam, flush); depthRangeChanged = true; @@ -793,6 +808,7 @@ public class RenderManager { // rest of the scene's objects. Consequently, they are sorted // back-to-front. if (!rq.isQueueEmpty(Bucket.Transparent)) { + if (prof!=null) prof.vpStep(VpStep.RenderBucket, vp, Bucket.Transparent); if (depthRangeChanged) { renderer.setDepthRange(0, 1); depthRangeChanged = false; @@ -802,6 +818,7 @@ public class RenderManager { } if (!rq.isQueueEmpty(Bucket.Gui)) { + if (prof!=null) prof.vpStep(VpStep.RenderBucket, vp, Bucket.Gui); renderer.setDepthRange(0, 0); setCamera(cam, true); rq.renderQueue(Bucket.Gui, this, cam, flush); @@ -828,6 +845,8 @@ public class RenderManager { * @see #setHandleTranslucentBucket(boolean) */ public void renderTranslucentQueue(ViewPort vp) { + if (prof!=null) prof.vpStep(VpStep.RenderBucket, vp, Bucket.Translucent); + RenderQueue rq = vp.getQueue(); if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) { rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true); @@ -963,6 +982,8 @@ public class RenderManager { if (!vp.isEnabled()) { return; } + if (prof!=null) prof.vpStep(VpStep.BeginRender, vp, null); + SafeArrayList processors = vp.getProcessors(); if (processors.isEmpty()) { processors = null; @@ -988,20 +1009,24 @@ public class RenderManager { vp.isClearStencil()); } + if (prof!=null) prof.vpStep(VpStep.RenderScene, vp, null); List scenes = vp.getScenes(); for (int i = scenes.size() - 1; i >= 0; i--) { renderScene(scenes.get(i), vp); } if (processors != null) { + if (prof!=null) prof.vpStep(VpStep.PostQueue, vp, null); for (SceneProcessor proc : processors.getArray()) { proc.postQueue(vp.getQueue()); } } + if (prof!=null) prof.vpStep(VpStep.FlushQueue, vp, null); flushQueue(vp); if (processors != null) { + if (prof!=null) prof.vpStep(VpStep.PostFrame, vp, null); for (SceneProcessor proc : processors.getArray()) { proc.postFrame(vp.getOutputFrameBuffer()); } @@ -1010,6 +1035,8 @@ public class RenderManager { renderTranslucentQueue(vp); // clear any remaining spatials that were not rendered. clearQueue(vp); + + if (prof!=null) prof.vpStep(VpStep.EndRender, vp, null); } public void setUsingShaders(boolean usingShaders) { @@ -1037,18 +1064,23 @@ public class RenderManager { this.shader = renderer.getCaps().contains(Caps.GLSL100); uniformBindingManager.newFrame(); + if (prof!=null) prof.appStep(AppStep.RenderPreviewViewPorts); for (int i = 0; i < preViewPorts.size(); i++) { ViewPort vp = preViewPorts.get(i); if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive) { renderViewPort(vp, tpf); } } + + if (prof!=null) prof.appStep(AppStep.RenderMainViewPorts); for (int i = 0; i < viewPorts.size(); i++) { ViewPort vp = viewPorts.get(i); if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive) { renderViewPort(vp, tpf); } } + + if (prof!=null) prof.appStep(AppStep.RenderPostViewPorts); for (int i = 0; i < postViewPorts.size(); i++) { ViewPort vp = postViewPorts.get(i); if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive) {