parent
6b33ae470a
commit
12fe7ba035
@ -0,0 +1,288 @@ |
|||||||
|
package com.jme3.post; |
||||||
|
|
||||||
|
import com.jme3.profile.*; |
||||||
|
import com.jme3.renderer.Renderer; |
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import com.jme3.renderer.queue.RenderQueue; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Nehon on 25/01/2017. |
||||||
|
*/ |
||||||
|
public class DetailedProfiler implements AppProfiler { |
||||||
|
|
||||||
|
private final static int MAX_FRAMES = 100; |
||||||
|
private Map<String, StatLine> data; |
||||||
|
private Map<String, StatLine> pool; |
||||||
|
private long startFrame; |
||||||
|
private static int currentFrame = 0; |
||||||
|
private String prevPath = null; |
||||||
|
private boolean frameEnded = false; |
||||||
|
private Renderer renderer; |
||||||
|
private boolean ongoingGpuProfiling = false; |
||||||
|
|
||||||
|
|
||||||
|
private String curAppPath = null; |
||||||
|
private String curVpPath = null; |
||||||
|
private String curSpPath = null; |
||||||
|
private VpStep lastVpStep = null; |
||||||
|
|
||||||
|
private StringBuilder path = new StringBuilder(256); |
||||||
|
private StringBuilder vpPath = new StringBuilder(256); |
||||||
|
|
||||||
|
private Deque<Integer> idsPool = new ArrayDeque<>(100); |
||||||
|
|
||||||
|
StatLine frameTime; |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void appStep(AppStep step) { |
||||||
|
curAppPath = step.name(); |
||||||
|
|
||||||
|
if (step == AppStep.BeginFrame) { |
||||||
|
if (data == null) { |
||||||
|
data = new LinkedHashMap<>(); |
||||||
|
pool = new HashMap<>(); |
||||||
|
frameTime = new StatLine(currentFrame); |
||||||
|
} |
||||||
|
if (frameTime.isActive()) { |
||||||
|
frameTime.setValueCpu(System.nanoTime() - frameTime.getValueCpu()); |
||||||
|
frameTime.closeFrame(); |
||||||
|
|
||||||
|
} |
||||||
|
frameTime.setNewFrameValueCpu(System.nanoTime()); |
||||||
|
|
||||||
|
frameEnded = false; |
||||||
|
for (StatLine statLine : data.values()) { |
||||||
|
for (Iterator<Integer> i = statLine.taskIds.iterator(); i.hasNext(); ) { |
||||||
|
int id = i.next(); |
||||||
|
if (renderer.isTaskResultAvailable(id)) { |
||||||
|
long val = renderer.getProfilingTime(id); |
||||||
|
statLine.setValueGpu(val); |
||||||
|
i.remove(); |
||||||
|
idsPool.push(id); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
data.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
if (data != null) { |
||||||
|
String path = getPath(step.name()); |
||||||
|
if (step == AppStep.EndFrame) { |
||||||
|
if (frameEnded) { |
||||||
|
return; |
||||||
|
} |
||||||
|
addStep(path, System.nanoTime()); |
||||||
|
StatLine end = data.get(path); |
||||||
|
end.setValueCpu(System.nanoTime() - startFrame); |
||||||
|
frameEnded = true; |
||||||
|
} else { |
||||||
|
addStep(path, System.nanoTime()); |
||||||
|
} |
||||||
|
} |
||||||
|
if (step == AppStep.EndFrame) { |
||||||
|
|
||||||
|
closeFrame(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void closeFrame() { |
||||||
|
//close frame
|
||||||
|
if (data != null) { |
||||||
|
|
||||||
|
prevPath = null; |
||||||
|
|
||||||
|
for (StatLine statLine : data.values()) { |
||||||
|
statLine.closeFrame(); |
||||||
|
} |
||||||
|
currentFrame++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void vpStep(VpStep step, ViewPort vp, RenderQueue.Bucket bucket) { |
||||||
|
|
||||||
|
if (data != null) { |
||||||
|
vpPath.setLength(0); |
||||||
|
vpPath.append(vp.getName()).append("/").append((bucket == null ? step.name() : bucket.name() + " Bucket")); |
||||||
|
path.setLength(0); |
||||||
|
if ((lastVpStep == VpStep.PostQueue || lastVpStep == VpStep.PostFrame) && bucket != null) { |
||||||
|
path.append(curAppPath).append("/").append(curVpPath).append(curSpPath).append("/").append(vpPath); |
||||||
|
curVpPath = vpPath.toString(); |
||||||
|
} else { |
||||||
|
if (bucket != null) { |
||||||
|
path.append(curAppPath).append("/").append(curVpPath).append("/").append(bucket.name() + " Bucket"); |
||||||
|
} else { |
||||||
|
path.append(curAppPath).append("/").append(vpPath); |
||||||
|
curVpPath = vpPath.toString(); |
||||||
|
} |
||||||
|
} |
||||||
|
lastVpStep = step; |
||||||
|
|
||||||
|
addStep(path.toString(), System.nanoTime()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void spStep(SpStep step, String... additionalInfo) { |
||||||
|
|
||||||
|
if (data != null) { |
||||||
|
curSpPath = getPath("", additionalInfo); |
||||||
|
path.setLength(0); |
||||||
|
path.append(curAppPath).append("/").append(curVpPath).append(curSpPath); |
||||||
|
addStep(path.toString(), System.nanoTime()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public Map<String, StatLine> getStats() { |
||||||
|
if (data != null) { |
||||||
|
return data;//new LinkedHashMap<>(data);
|
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public double getAverageFrameTime() { |
||||||
|
return frameTime.getAverageCpu(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void addStep(String path, long value) { |
||||||
|
if (ongoingGpuProfiling && renderer != null) { |
||||||
|
renderer.stopProfiling(); |
||||||
|
ongoingGpuProfiling = false; |
||||||
|
} |
||||||
|
|
||||||
|
if (prevPath != null) { |
||||||
|
StatLine prevLine = data.get(prevPath); |
||||||
|
if (prevLine != null) { |
||||||
|
prevLine.setValueCpu(value - prevLine.getValueCpu()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StatLine line = pool.get(path); |
||||||
|
if (line == null) { |
||||||
|
line = new StatLine(currentFrame); |
||||||
|
pool.put(path, line); |
||||||
|
} |
||||||
|
data.put(path, line); |
||||||
|
line.setNewFrameValueCpu(value); |
||||||
|
if (renderer != null) { |
||||||
|
int id = getUnusedTaskId(); |
||||||
|
line.taskIds.add(id); |
||||||
|
renderer.startProfiling(id); |
||||||
|
} |
||||||
|
ongoingGpuProfiling = true; |
||||||
|
prevPath = path; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private String getPath(String step, String... subPath) { |
||||||
|
StringBuilder path = new StringBuilder(step); |
||||||
|
if (subPath != null) { |
||||||
|
for (String s : subPath) { |
||||||
|
path.append("/").append(s); |
||||||
|
} |
||||||
|
} |
||||||
|
return path.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setRenderer(Renderer renderer) { |
||||||
|
this.renderer = renderer; |
||||||
|
poolTaskIds(renderer); |
||||||
|
} |
||||||
|
|
||||||
|
private void poolTaskIds(Renderer renderer) { |
||||||
|
int[] ids = renderer.generateProfilingTasks(100); |
||||||
|
for (int id : ids) { |
||||||
|
idsPool.push(id); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private int getUnusedTaskId() { |
||||||
|
if (idsPool.isEmpty()) { |
||||||
|
poolTaskIds(renderer); |
||||||
|
} |
||||||
|
|
||||||
|
return idsPool.pop(); |
||||||
|
} |
||||||
|
|
||||||
|
public static class StatLine { |
||||||
|
private long[] cpuTimes = new long[MAX_FRAMES]; |
||||||
|
private long[] gpuTimes = new long[MAX_FRAMES]; |
||||||
|
private int startCursor = 0; |
||||||
|
private int cpuCursor = 0; |
||||||
|
private int gpuCursor = 0; |
||||||
|
private int cpuSum = 0; |
||||||
|
private int gpuSum = 0; |
||||||
|
private long lastValue = 0; |
||||||
|
private int nbFramesCpu; |
||||||
|
private int nbFramesGpu; |
||||||
|
List<Integer> taskIds = new ArrayList<>(); |
||||||
|
|
||||||
|
|
||||||
|
private StatLine(int currentFrame) { |
||||||
|
startCursor = currentFrame % MAX_FRAMES; |
||||||
|
cpuCursor = startCursor; |
||||||
|
gpuCursor = startCursor; |
||||||
|
} |
||||||
|
|
||||||
|
private void setNewFrameValueCpu(long value) { |
||||||
|
int newCursor = currentFrame % MAX_FRAMES; |
||||||
|
if (nbFramesCpu == 0) { |
||||||
|
startCursor = newCursor; |
||||||
|
} |
||||||
|
cpuCursor = newCursor; |
||||||
|
lastValue = value; |
||||||
|
} |
||||||
|
|
||||||
|
private void setValueCpu(long val) { |
||||||
|
lastValue = val; |
||||||
|
} |
||||||
|
|
||||||
|
private long getValueCpu() { |
||||||
|
return lastValue; |
||||||
|
} |
||||||
|
|
||||||
|
private void closeFrame() { |
||||||
|
if (isActive()) { |
||||||
|
cpuSum -= cpuTimes[cpuCursor]; |
||||||
|
cpuTimes[cpuCursor] = lastValue; |
||||||
|
cpuSum += lastValue; |
||||||
|
nbFramesCpu++; |
||||||
|
} else { |
||||||
|
nbFramesCpu = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void setValueGpu(long value) { |
||||||
|
gpuSum -= gpuTimes[gpuCursor]; |
||||||
|
gpuTimes[gpuCursor] = value; |
||||||
|
gpuSum += value; |
||||||
|
nbFramesGpu++; |
||||||
|
gpuCursor = (gpuCursor + 1) % MAX_FRAMES; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isActive() { |
||||||
|
return cpuCursor >= currentFrame % MAX_FRAMES - 1; |
||||||
|
} |
||||||
|
|
||||||
|
public double getAverageCpu() { |
||||||
|
if (nbFramesCpu == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return (double) cpuSum / (double) Math.min(nbFramesCpu, MAX_FRAMES); |
||||||
|
} |
||||||
|
|
||||||
|
public double getAverageGpu() { |
||||||
|
if (nbFramesGpu == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
return (double) gpuSum / (double) Math.min(nbFramesGpu, MAX_FRAMES); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,448 @@ |
|||||||
|
package com.jme3.post; |
||||||
|
|
||||||
|
import com.jme3.app.Application; |
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.app.state.BaseAppState; |
||||||
|
import com.jme3.font.BitmapFont; |
||||||
|
import com.jme3.font.BitmapText; |
||||||
|
import com.jme3.input.*; |
||||||
|
import com.jme3.input.controls.*; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.material.RenderState; |
||||||
|
import com.jme3.math.*; |
||||||
|
import com.jme3.profile.AppStep; |
||||||
|
import com.jme3.scene.*; |
||||||
|
import com.jme3.scene.shape.Quad; |
||||||
|
|
||||||
|
import java.text.DecimalFormat; |
||||||
|
import java.text.DecimalFormatSymbols; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import static com.jme3.post.DetailedProfiler.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Nehon on 25/01/2017. |
||||||
|
*/ |
||||||
|
public class DetailedProfilerState extends BaseAppState { |
||||||
|
|
||||||
|
private static final int PANEL_WIDTH = 400; |
||||||
|
private static final int PADDING = 10; |
||||||
|
private static final int LINE_HEIGHT = 12; |
||||||
|
private static final int HEADER_HEIGHT = 100; |
||||||
|
private static final float REFRESH_TIME = 1.0f; |
||||||
|
private static final String TOGGLE_KEY = "Toggle_Detailed_Profiler"; |
||||||
|
private static final String CLICK_KEY = "Click_Detailed_Profiler"; |
||||||
|
private static final String INSIGNIFICANT = "Hide insignificant stat"; |
||||||
|
private DetailedProfiler prof = new DetailedProfiler(); |
||||||
|
|
||||||
|
private float time = 0; |
||||||
|
private BitmapFont font; |
||||||
|
private BitmapFont bigFont; |
||||||
|
private Node ui = new Node("Stats ui"); |
||||||
|
private Map<String, StatLineView> lines = new HashMap<>(); |
||||||
|
private double totalTimeCpu; |
||||||
|
private double totalTimeGpu; |
||||||
|
private int maxLevel = 0; |
||||||
|
|
||||||
|
private BitmapText frameTimeValue; |
||||||
|
private BitmapText frameCpuTimeValue; |
||||||
|
private BitmapText frameGpuTimeValue; |
||||||
|
private BitmapText hideInsignificantField; |
||||||
|
|
||||||
|
private BitmapText selectedField; |
||||||
|
private double selectedValueCpu = 0; |
||||||
|
private double selectedValueGpu = 0; |
||||||
|
private boolean hideInsignificant = false; |
||||||
|
|
||||||
|
private StatLineView rootLine; |
||||||
|
private int height = 0; |
||||||
|
private DecimalFormat df = new DecimalFormat("##0.00", new DecimalFormatSymbols(Locale.US)); |
||||||
|
|
||||||
|
private ColorRGBA dimmedWhite = ColorRGBA.White.mult(0.7f); |
||||||
|
private ColorRGBA dimmedGreen = ColorRGBA.Green.mult(0.7f); |
||||||
|
private ColorRGBA dimmedOrange = ColorRGBA.Orange.mult(0.7f); |
||||||
|
private ColorRGBA dimmedRed = ColorRGBA.Red.mult(0.7f); |
||||||
|
|
||||||
|
public DetailedProfilerState() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initialize(Application app) { |
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); |
||||||
|
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); |
||||||
|
Geometry darkenStats = new Geometry("StatsDarken", new Quad(PANEL_WIDTH, app.getCamera().getHeight())); |
||||||
|
darkenStats.setMaterial(mat); |
||||||
|
darkenStats.setLocalTranslation(0, -app.getCamera().getHeight(), -1); |
||||||
|
|
||||||
|
ui.attachChild(darkenStats); |
||||||
|
ui.setLocalTranslation(app.getCamera().getWidth() - PANEL_WIDTH, app.getCamera().getHeight(), 0); |
||||||
|
font = app.getAssetManager().loadFont("Interface/Fonts/Console.fnt"); |
||||||
|
bigFont = app.getAssetManager().loadFont("Interface/Fonts/Default.fnt"); |
||||||
|
prof.setRenderer(app.getRenderer()); |
||||||
|
rootLine = new StatLineView("Frame"); |
||||||
|
rootLine.attachTo(ui); |
||||||
|
|
||||||
|
BitmapText frameLabel = new BitmapText(bigFont); |
||||||
|
frameLabel.setText("Total Frame Time: "); |
||||||
|
ui.attachChild(frameLabel); |
||||||
|
frameLabel.setLocalTranslation(new Vector3f(PANEL_WIDTH / 2 - bigFont.getLineWidth(frameLabel.getText()), -PADDING, 0)); |
||||||
|
|
||||||
|
BitmapText cpuLabel = new BitmapText(bigFont); |
||||||
|
cpuLabel.setText("CPU"); |
||||||
|
ui.attachChild(cpuLabel); |
||||||
|
cpuLabel.setLocalTranslation(PANEL_WIDTH / 4 - bigFont.getLineWidth(cpuLabel.getText()) / 2, -PADDING - 30, 0); |
||||||
|
|
||||||
|
BitmapText gpuLabel = new BitmapText(bigFont); |
||||||
|
gpuLabel.setText("GPU"); |
||||||
|
ui.attachChild(gpuLabel); |
||||||
|
gpuLabel.setLocalTranslation(3 * PANEL_WIDTH / 4 - bigFont.getLineWidth(gpuLabel.getText()) / 2, -PADDING - 30, 0); |
||||||
|
|
||||||
|
frameTimeValue = new BitmapText(bigFont); |
||||||
|
frameCpuTimeValue = new BitmapText(bigFont); |
||||||
|
frameGpuTimeValue = new BitmapText(bigFont); |
||||||
|
|
||||||
|
selectedField = new BitmapText(font); |
||||||
|
selectedField.setText("Selected: "); |
||||||
|
selectedField.setLocalTranslation(PANEL_WIDTH / 2, -PADDING - 75, 0); |
||||||
|
selectedField.setColor(ColorRGBA.Yellow); |
||||||
|
|
||||||
|
|
||||||
|
ui.attachChild(frameTimeValue); |
||||||
|
ui.attachChild(frameCpuTimeValue); |
||||||
|
ui.attachChild(frameGpuTimeValue); |
||||||
|
ui.attachChild(selectedField); |
||||||
|
|
||||||
|
hideInsignificantField = new BitmapText(font); |
||||||
|
hideInsignificantField.setText("O " + INSIGNIFICANT); |
||||||
|
hideInsignificantField.setLocalTranslation(PADDING, -PADDING - 75, 0); |
||||||
|
ui.attachChild(hideInsignificantField); |
||||||
|
|
||||||
|
final InputManager inputManager = app.getInputManager(); |
||||||
|
if (inputManager != null) { |
||||||
|
inputManager.addMapping(TOGGLE_KEY, new KeyTrigger(KeyInput.KEY_F6)); |
||||||
|
inputManager.addMapping(CLICK_KEY, new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); |
||||||
|
inputManager.addListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void onAction(String name, boolean isPressed, float tpf) { |
||||||
|
if (name.equals(TOGGLE_KEY) && isPressed) { |
||||||
|
setEnabled(!isEnabled()); |
||||||
|
} |
||||||
|
if (isEnabled() && name.equals(CLICK_KEY) && isPressed) { |
||||||
|
handleClick(inputManager.getCursorPosition()); |
||||||
|
} |
||||||
|
} |
||||||
|
}, TOGGLE_KEY, CLICK_KEY); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void cleanup(Application app) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void update(float tpf) { |
||||||
|
time += tpf; |
||||||
|
} |
||||||
|
|
||||||
|
private void displayData(Map<String, DetailedProfiler.StatLine> data) { |
||||||
|
if (data == null || data.isEmpty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
for (StatLineView statLine : lines.values()) { |
||||||
|
statLine.reset(); |
||||||
|
statLine.removeFromParent(); |
||||||
|
} |
||||||
|
rootLine.reset(); |
||||||
|
maxLevel = 0; |
||||||
|
for (String path : data.keySet()) { |
||||||
|
if (path.equals("EndFrame")) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
maxLevel = Math.max(maxLevel, path.split("/").length); |
||||||
|
StatLineView line = getStatLineView(path); |
||||||
|
StatLine statLine = data.get(path); |
||||||
|
line.updateValues(statLine.getAverageCpu(), statLine.getAverageGpu()); |
||||||
|
String parent = getParent(path); |
||||||
|
while (parent != null) { |
||||||
|
StatLineView parentView = getStatLineView(parent); |
||||||
|
parentView.updateValues(statLine.getAverageCpu(), statLine.getAverageGpu()); |
||||||
|
parentView.children.add(line); |
||||||
|
line.attachTo(ui); |
||||||
|
line = parentView; |
||||||
|
parent = getParent(parent); |
||||||
|
} |
||||||
|
rootLine.children.add(line); |
||||||
|
line.attachTo(ui); |
||||||
|
rootLine.updateValues(statLine.getAverageCpu(), statLine.getAverageGpu()); |
||||||
|
} |
||||||
|
|
||||||
|
totalTimeCpu = rootLine.cpuValue; |
||||||
|
totalTimeGpu = rootLine.gpuValue + data.get("EndFrame").getAverageGpu(); |
||||||
|
|
||||||
|
layout(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void layout() { |
||||||
|
height = 0; |
||||||
|
selectedValueCpu = 0; |
||||||
|
selectedValueGpu = 0; |
||||||
|
rootLine.layout(0); |
||||||
|
|
||||||
|
frameTimeValue.setText(df.format(getMsFromNs(prof.getAverageFrameTime())) + "ms"); |
||||||
|
frameTimeValue.setLocalTranslation(PANEL_WIDTH / 2, -PADDING, 0); |
||||||
|
setColor(frameTimeValue, prof.getAverageFrameTime(), totalTimeCpu, false, false); |
||||||
|
|
||||||
|
frameCpuTimeValue.setText(df.format(getMsFromNs(totalTimeCpu)) + "ms"); |
||||||
|
frameCpuTimeValue.setLocalTranslation(new Vector3f(PANEL_WIDTH / 4 - bigFont.getLineWidth(frameCpuTimeValue.getText()) / 2, -PADDING - 50, 0)); |
||||||
|
setColor(frameCpuTimeValue, totalTimeCpu, totalTimeCpu, false, false); |
||||||
|
|
||||||
|
frameGpuTimeValue.setText(df.format(getMsFromNs(totalTimeGpu)) + "ms"); |
||||||
|
frameGpuTimeValue.setLocalTranslation(new Vector3f(3 * PANEL_WIDTH / 4 - bigFont.getLineWidth(frameGpuTimeValue.getText()) / 2, -PADDING - 50, 0)); |
||||||
|
setColor(frameGpuTimeValue, totalTimeGpu, totalTimeGpu, false, false); |
||||||
|
|
||||||
|
selectedField.setText("Selected: " + df.format(getMsFromNs(selectedValueCpu)) + "ms / " + df.format(getMsFromNs(selectedValueGpu)) + "ms"); |
||||||
|
|
||||||
|
selectedField.setLocalTranslation(3 * PANEL_WIDTH / 4 - font.getLineWidth(selectedField.getText()) / 2, -PADDING - 75, 0); |
||||||
|
} |
||||||
|
|
||||||
|
private StatLineView getStatLineView(String path) { |
||||||
|
StatLineView line = lines.get(path); |
||||||
|
|
||||||
|
if (line == null) { |
||||||
|
line = new StatLineView(getLeaf(path)); |
||||||
|
lines.put(path, line); |
||||||
|
line.attachTo(ui); |
||||||
|
} |
||||||
|
return line; |
||||||
|
} |
||||||
|
|
||||||
|
private String getLeaf(String path) { |
||||||
|
int idx = path.lastIndexOf("/"); |
||||||
|
return idx >= 0 ? path.substring(idx + 1) : path; |
||||||
|
} |
||||||
|
|
||||||
|
private String getParent(String path) { |
||||||
|
int idx = path.lastIndexOf("/"); |
||||||
|
return idx >= 0 ? path.substring(0, idx) : null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void postRender() { |
||||||
|
if (time > REFRESH_TIME) { |
||||||
|
prof.appStep(AppStep.EndFrame); |
||||||
|
Map<String, StatLine> data = prof.getStats(); |
||||||
|
displayData(data); |
||||||
|
time = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private double getMsFromNs(double time) { |
||||||
|
return time / 1000000.0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onEnable() { |
||||||
|
getApplication().setAppProfiler(prof); |
||||||
|
((SimpleApplication) getApplication()).getGuiNode().attachChild(ui); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onDisable() { |
||||||
|
getApplication().setAppProfiler(null); |
||||||
|
ui.removeFromParent(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean setColor(BitmapText t, double value, double totalTime, boolean isParent, boolean expended) { |
||||||
|
|
||||||
|
boolean dimmed = isParent && expended; |
||||||
|
boolean insignificant = false; |
||||||
|
|
||||||
|
if (value > 1000000000.0 / 60.0) { |
||||||
|
t.setColor(dimmed ? dimmedOrange : ColorRGBA.Orange); |
||||||
|
} else if (value > 1000000000f / 30f) { |
||||||
|
t.setColor(dimmed ? dimmedRed : ColorRGBA.Red); |
||||||
|
} else if (value > totalTime / 3) { |
||||||
|
t.setColor(dimmed ? dimmedGreen : ColorRGBA.Green); |
||||||
|
} else if (value < 30000) { |
||||||
|
t.setColor(ColorRGBA.DarkGray); |
||||||
|
insignificant = true; |
||||||
|
} else { |
||||||
|
t.setColor(dimmed ? dimmedWhite : ColorRGBA.White); |
||||||
|
} |
||||||
|
return insignificant; |
||||||
|
} |
||||||
|
|
||||||
|
private void handleClick(Vector2f pos) { |
||||||
|
|
||||||
|
Vector3f lp = hideInsignificantField.getWorldTranslation(); |
||||||
|
float width = font.getLineWidth(hideInsignificantField.getText()); |
||||||
|
if (pos.x > lp.x && pos.x < (lp.x + width) |
||||||
|
&& pos.y < lp.y && pos.y > lp.y - LINE_HEIGHT) { |
||||||
|
hideInsignificant = !hideInsignificant; |
||||||
|
hideInsignificantField.setText((hideInsignificant ? "X " : "O ") + INSIGNIFICANT); |
||||||
|
if (!hideInsignificant) { |
||||||
|
rootLine.setExpended(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
rootLine.onClick(pos); |
||||||
|
for (StatLineView statLineView : lines.values()) { |
||||||
|
statLineView.onClick(pos); |
||||||
|
} |
||||||
|
layout(); |
||||||
|
} |
||||||
|
|
||||||
|
private class StatLineView { |
||||||
|
BitmapText label; |
||||||
|
BitmapText cpuText; |
||||||
|
BitmapText gpuText; |
||||||
|
BitmapText checkBox; |
||||||
|
double cpuValue; |
||||||
|
double gpuValue; |
||||||
|
private boolean expended = true; |
||||||
|
private boolean visible = true; |
||||||
|
private boolean selected = false; |
||||||
|
String text; |
||||||
|
|
||||||
|
Set<StatLineView> children = new LinkedHashSet<>(); |
||||||
|
|
||||||
|
public StatLineView(String label) { |
||||||
|
this.text = label; |
||||||
|
this.label = new BitmapText(font); |
||||||
|
this.checkBox = new BitmapText(font); |
||||||
|
this.checkBox.setText("O"); |
||||||
|
this.label.setText("- " + label); |
||||||
|
this.cpuText = new BitmapText(font); |
||||||
|
this.gpuText = new BitmapText(font); |
||||||
|
} |
||||||
|
|
||||||
|
public void onClick(Vector2f pos) { |
||||||
|
|
||||||
|
if (!visible) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Vector3f lp = label.getWorldTranslation(); |
||||||
|
Vector3f cp = checkBox.getWorldTranslation(); |
||||||
|
if (pos.x > cp.x |
||||||
|
&& pos.y < lp.y && pos.y > lp.y - LINE_HEIGHT) { |
||||||
|
|
||||||
|
float width = font.getLineWidth(checkBox.getText()); |
||||||
|
if (pos.x >= cp.x && pos.x <= (cp.x + width)) { |
||||||
|
selected = !selected; |
||||||
|
if (selected) { |
||||||
|
checkBox.setText("X"); |
||||||
|
} else { |
||||||
|
checkBox.setText("O"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
setExpended(!expended); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void setExpended(boolean expended) { |
||||||
|
this.expended = expended; |
||||||
|
if (expended) { |
||||||
|
label.setText("- " + text); |
||||||
|
} else { |
||||||
|
label.setText("+ " + text); |
||||||
|
} |
||||||
|
for (StatLineView child : children) { |
||||||
|
child.setVisible(expended); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void layout(int indent) { |
||||||
|
|
||||||
|
boolean insignificant; |
||||||
|
cpuText.setText(df.format(getMsFromNs(cpuValue)) + "ms /"); |
||||||
|
insignificant = setColor(cpuText, cpuValue, totalTimeCpu, !children.isEmpty(), expended); |
||||||
|
gpuText.setText(" " + df.format(getMsFromNs(gpuValue)) + "ms"); |
||||||
|
insignificant &= setColor(gpuText, gpuValue, totalTimeGpu, !children.isEmpty(), expended); |
||||||
|
|
||||||
|
if (insignificant && hideInsignificant) { |
||||||
|
setVisible(false); |
||||||
|
} |
||||||
|
|
||||||
|
if (!visible) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (selected) { |
||||||
|
label.setColor(ColorRGBA.Yellow); |
||||||
|
selectedValueCpu += cpuValue; |
||||||
|
selectedValueGpu += gpuValue; |
||||||
|
} else { |
||||||
|
label.setColor(ColorRGBA.White); |
||||||
|
} |
||||||
|
|
||||||
|
int y = -(height * LINE_HEIGHT + HEADER_HEIGHT); |
||||||
|
|
||||||
|
label.setLocalTranslation(PADDING + indent * PADDING, y, 0); |
||||||
|
float gpuPos = PANEL_WIDTH - font.getLineWidth(gpuText.getText()) - PADDING * (maxLevel - indent + 1); |
||||||
|
cpuText.setLocalTranslation(gpuPos - font.getLineWidth(cpuText.getText()), y, 0); |
||||||
|
gpuText.setLocalTranslation(gpuPos, y, 0); |
||||||
|
|
||||||
|
checkBox.setLocalTranslation(3, y, 0); |
||||||
|
height++; |
||||||
|
for (StatLineView child : children) { |
||||||
|
child.layout(indent + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void updateValues(double cpu, double gpu) { |
||||||
|
cpuValue += cpu; |
||||||
|
gpuValue += gpu; |
||||||
|
} |
||||||
|
|
||||||
|
public void attachTo(Node node) { |
||||||
|
node.attachChild(label); |
||||||
|
node.attachChild(cpuText); |
||||||
|
node.attachChild(gpuText); |
||||||
|
node.attachChild(checkBox); |
||||||
|
} |
||||||
|
|
||||||
|
public void removeFromParent() { |
||||||
|
label.removeFromParent(); |
||||||
|
cpuText.removeFromParent(); |
||||||
|
gpuText.removeFromParent(); |
||||||
|
checkBox.removeFromParent(); |
||||||
|
} |
||||||
|
|
||||||
|
public void reset() { |
||||||
|
children.clear(); |
||||||
|
cpuValue = 0; |
||||||
|
gpuValue = 0; |
||||||
|
} |
||||||
|
|
||||||
|
public void setVisible(boolean visible) { |
||||||
|
this.visible = visible; |
||||||
|
label.setCullHint(visible ? Spatial.CullHint.Dynamic : Spatial.CullHint.Always); |
||||||
|
cpuText.setCullHint(visible ? Spatial.CullHint.Dynamic : Spatial.CullHint.Always); |
||||||
|
gpuText.setCullHint(visible ? Spatial.CullHint.Dynamic : Spatial.CullHint.Always); |
||||||
|
checkBox.setCullHint(visible ? Spatial.CullHint.Dynamic : Spatial.CullHint.Always); |
||||||
|
|
||||||
|
|
||||||
|
for (StatLineView child : children) { |
||||||
|
child.setVisible(visible && expended); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return label.getText() + " - " + df.format(getMsFromNs(cpuValue)) + "ms / " + df.format(getMsFromNs(gpuValue)) + "ms"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
Loading…
Reference in new issue