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.
experimental
pspeed42 11 years ago
parent 3d32b012c2
commit a517130528
  1. 31
      jme3-core/src/main/java/com/jme3/app/Application.java
  2. 10
      jme3-core/src/main/java/com/jme3/app/SimpleApplication.java
  3. 32
      jme3-core/src/main/java/com/jme3/renderer/RenderManager.java

@ -38,6 +38,8 @@ import com.jme3.audio.AudioRenderer;
import com.jme3.audio.Listener; import com.jme3.audio.Listener;
import com.jme3.input.*; import com.jme3.input.*;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.profile.AppProfiler;
import com.jme3.profile.AppStep;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer; import com.jme3.renderer.Renderer;
@ -91,6 +93,8 @@ public class Application implements SystemListener {
protected InputManager inputManager; protected InputManager inputManager;
protected AppStateManager stateManager; protected AppStateManager stateManager;
protected AppProfiler prof;
private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>(); private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>();
/** /**
@ -250,6 +254,11 @@ public class Application implements SystemListener {
renderManager = new RenderManager(renderer); renderManager = new RenderManager(renderer);
//Remy - 09/14/2010 setted the timer in the renderManager //Remy - 09/14/2010 setted the timer in the renderManager
renderManager.setTimer(timer); renderManager.setTimer(timer);
if (prof != null) {
renderManager.setAppProfiler(prof);
}
viewPort = renderManager.createMainView("Default", cam); viewPort = renderManager.createMainView("Default", cam);
viewPort.setClearFlags(true, true, true); viewPort.setClearFlags(true, true, true);
@ -387,6 +396,25 @@ public class Application implements SystemListener {
context.create(false); 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. * Initializes the application's canvas for use.
* <p> * <p>
@ -595,6 +623,7 @@ public class Application implements SystemListener {
// Make sure the audio renderer is available to callables // Make sure the audio renderer is available to callables
AudioContext.setAudioRenderer(audioRenderer); AudioContext.setAudioRenderer(audioRenderer);
if (prof!=null) prof.appStep(AppStep.QueuedTasks);
runQueuedTasks(); runQueuedTasks();
if (speed == 0 || paused) if (speed == 0 || paused)
@ -603,10 +632,12 @@ public class Application implements SystemListener {
timer.update(); timer.update();
if (inputEnabled){ if (inputEnabled){
if (prof!=null) prof.appStep(AppStep.ProcessInput);
inputManager.update(timer.getTimePerFrame()); inputManager.update(timer.getTimePerFrame());
} }
if (audioRenderer != null){ if (audioRenderer != null){
if (prof!=null) prof.appStep(AppStep.ProcessAudio);
audioRenderer.update(timer.getTimePerFrame()); audioRenderer.update(timer.getTimePerFrame());
} }

@ -38,6 +38,7 @@ import com.jme3.input.FlyByCamera;
import com.jme3.input.KeyInput; import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger; import com.jme3.input.controls.KeyTrigger;
import com.jme3.profile.AppStep;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Node; import com.jme3.scene.Node;
@ -228,6 +229,8 @@ public abstract class SimpleApplication extends Application {
@Override @Override
public void update() { public void update() {
if (prof!=null) prof.appStep(AppStep.BeginFrame);
super.update(); // makes sure to execute AppTasks super.update(); // makes sure to execute AppTasks
if (speed == 0 || paused) { if (speed == 0 || paused) {
return; return;
@ -236,11 +239,13 @@ public abstract class SimpleApplication extends Application {
float tpf = timer.getTimePerFrame() * speed; float tpf = timer.getTimePerFrame() * speed;
// update states // update states
if (prof!=null) prof.appStep(AppStep.StateManagerUpdate);
stateManager.update(tpf); stateManager.update(tpf);
// simple update and root node // simple update and root node
simpleUpdate(tpf); simpleUpdate(tpf);
if (prof!=null) prof.appStep(AppStep.SpatialUpdate);
rootNode.updateLogicalState(tpf); rootNode.updateLogicalState(tpf);
guiNode.updateLogicalState(tpf); guiNode.updateLogicalState(tpf);
@ -248,10 +253,15 @@ public abstract class SimpleApplication extends Application {
guiNode.updateGeometricState(); guiNode.updateGeometricState();
// render states // render states
if (prof!=null) prof.appStep(AppStep.StateManagerRender);
stateManager.render(renderManager); stateManager.render(renderManager);
if (prof!=null) prof.appStep(AppStep.RenderFrame);
renderManager.render(tpf, context.isRenderable()); renderManager.render(tpf, context.isRenderable());
simpleRender(renderManager); simpleRender(renderManager);
stateManager.postRender(); stateManager.postRender();
if (prof!=null) prof.appStep(AppStep.EndFrame);
} }
public void setDisplayFps(boolean show) { public void setDisplayFps(boolean show) {

@ -37,6 +37,9 @@ import com.jme3.material.RenderState;
import com.jme3.material.Technique; import com.jme3.material.Technique;
import com.jme3.math.*; import com.jme3.math.*;
import com.jme3.post.SceneProcessor; 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.GeometryList;
import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue;
import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.renderer.queue.RenderQueue.Bucket;
@ -80,6 +83,7 @@ public class RenderManager {
private Matrix4f orthoMatrix = new Matrix4f(); private Matrix4f orthoMatrix = new Matrix4f();
private String tmpTech; private String tmpTech;
private boolean handleTranlucentBucket = true; private boolean handleTranlucentBucket = true;
private AppProfiler prof;
/** /**
* Create a high-level rendering interface over the * Create a high-level rendering interface over the
@ -377,6 +381,15 @@ public class RenderManager {
uniformBindingManager.setTimer(timer); 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. * Returns the forced technique name set.
* *
@ -779,10 +792,12 @@ public class RenderManager {
// render opaque objects with default depth range // render opaque objects with default depth range
// opaque objects are sorted front-to-back, reducing overdraw // 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); rq.renderQueue(Bucket.Opaque, this, cam, flush);
// render the sky, with depth range set to the farthest // render the sky, with depth range set to the farthest
if (!rq.isQueueEmpty(Bucket.Sky)) { if (!rq.isQueueEmpty(Bucket.Sky)) {
if (prof!=null) prof.vpStep(VpStep.RenderBucket, vp, Bucket.Sky);
renderer.setDepthRange(1, 1); renderer.setDepthRange(1, 1);
rq.renderQueue(Bucket.Sky, this, cam, flush); rq.renderQueue(Bucket.Sky, this, cam, flush);
depthRangeChanged = true; depthRangeChanged = true;
@ -793,6 +808,7 @@ public class RenderManager {
// rest of the scene's objects. Consequently, they are sorted // rest of the scene's objects. Consequently, they are sorted
// back-to-front. // back-to-front.
if (!rq.isQueueEmpty(Bucket.Transparent)) { if (!rq.isQueueEmpty(Bucket.Transparent)) {
if (prof!=null) prof.vpStep(VpStep.RenderBucket, vp, Bucket.Transparent);
if (depthRangeChanged) { if (depthRangeChanged) {
renderer.setDepthRange(0, 1); renderer.setDepthRange(0, 1);
depthRangeChanged = false; depthRangeChanged = false;
@ -802,6 +818,7 @@ public class RenderManager {
} }
if (!rq.isQueueEmpty(Bucket.Gui)) { if (!rq.isQueueEmpty(Bucket.Gui)) {
if (prof!=null) prof.vpStep(VpStep.RenderBucket, vp, Bucket.Gui);
renderer.setDepthRange(0, 0); renderer.setDepthRange(0, 0);
setCamera(cam, true); setCamera(cam, true);
rq.renderQueue(Bucket.Gui, this, cam, flush); rq.renderQueue(Bucket.Gui, this, cam, flush);
@ -828,6 +845,8 @@ public class RenderManager {
* @see #setHandleTranslucentBucket(boolean) * @see #setHandleTranslucentBucket(boolean)
*/ */
public void renderTranslucentQueue(ViewPort vp) { public void renderTranslucentQueue(ViewPort vp) {
if (prof!=null) prof.vpStep(VpStep.RenderBucket, vp, Bucket.Translucent);
RenderQueue rq = vp.getQueue(); RenderQueue rq = vp.getQueue();
if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) { if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {
rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true); rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);
@ -963,6 +982,8 @@ public class RenderManager {
if (!vp.isEnabled()) { if (!vp.isEnabled()) {
return; return;
} }
if (prof!=null) prof.vpStep(VpStep.BeginRender, vp, null);
SafeArrayList<SceneProcessor> processors = vp.getProcessors(); SafeArrayList<SceneProcessor> processors = vp.getProcessors();
if (processors.isEmpty()) { if (processors.isEmpty()) {
processors = null; processors = null;
@ -988,20 +1009,24 @@ public class RenderManager {
vp.isClearStencil()); vp.isClearStencil());
} }
if (prof!=null) prof.vpStep(VpStep.RenderScene, vp, null);
List<Spatial> scenes = vp.getScenes(); List<Spatial> scenes = vp.getScenes();
for (int i = scenes.size() - 1; i >= 0; i--) { for (int i = scenes.size() - 1; i >= 0; i--) {
renderScene(scenes.get(i), vp); renderScene(scenes.get(i), vp);
} }
if (processors != null) { if (processors != null) {
if (prof!=null) prof.vpStep(VpStep.PostQueue, vp, null);
for (SceneProcessor proc : processors.getArray()) { for (SceneProcessor proc : processors.getArray()) {
proc.postQueue(vp.getQueue()); proc.postQueue(vp.getQueue());
} }
} }
if (prof!=null) prof.vpStep(VpStep.FlushQueue, vp, null);
flushQueue(vp); flushQueue(vp);
if (processors != null) { if (processors != null) {
if (prof!=null) prof.vpStep(VpStep.PostFrame, vp, null);
for (SceneProcessor proc : processors.getArray()) { for (SceneProcessor proc : processors.getArray()) {
proc.postFrame(vp.getOutputFrameBuffer()); proc.postFrame(vp.getOutputFrameBuffer());
} }
@ -1010,6 +1035,8 @@ public class RenderManager {
renderTranslucentQueue(vp); renderTranslucentQueue(vp);
// clear any remaining spatials that were not rendered. // clear any remaining spatials that were not rendered.
clearQueue(vp); clearQueue(vp);
if (prof!=null) prof.vpStep(VpStep.EndRender, vp, null);
} }
public void setUsingShaders(boolean usingShaders) { public void setUsingShaders(boolean usingShaders) {
@ -1037,18 +1064,23 @@ public class RenderManager {
this.shader = renderer.getCaps().contains(Caps.GLSL100); this.shader = renderer.getCaps().contains(Caps.GLSL100);
uniformBindingManager.newFrame(); uniformBindingManager.newFrame();
if (prof!=null) prof.appStep(AppStep.RenderPreviewViewPorts);
for (int i = 0; i < preViewPorts.size(); i++) { for (int i = 0; i < preViewPorts.size(); i++) {
ViewPort vp = preViewPorts.get(i); ViewPort vp = preViewPorts.get(i);
if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive) { if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive) {
renderViewPort(vp, tpf); renderViewPort(vp, tpf);
} }
} }
if (prof!=null) prof.appStep(AppStep.RenderMainViewPorts);
for (int i = 0; i < viewPorts.size(); i++) { for (int i = 0; i < viewPorts.size(); i++) {
ViewPort vp = viewPorts.get(i); ViewPort vp = viewPorts.get(i);
if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive) { if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive) {
renderViewPort(vp, tpf); renderViewPort(vp, tpf);
} }
} }
if (prof!=null) prof.appStep(AppStep.RenderPostViewPorts);
for (int i = 0; i < postViewPorts.size(); i++) { for (int i = 0; i < postViewPorts.size(); i++) {
ViewPort vp = postViewPorts.get(i); ViewPort vp = postViewPorts.get(i);
if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive) { if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive) {

Loading…
Cancel
Save