Explorar el Código

Added a DetailedProfiler and its associated AppState for detailed time profiling

Nehon hace 8 años
padre
commit
12fe7ba035

+ 288 - 0
jme3-core/src/main/java/com/jme3/post/DetailedProfiler.java

@@ -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);
+        }
+    }
+
+}

+ 448 - 0
jme3-core/src/main/java/com/jme3/post/DetailedProfilerState.java

@@ -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";
+        }
+
+
+    }
+}
+

+ 1 - 1
jme3-examples/src/main/java/jme3test/post/SSAOUI.java

@@ -52,7 +52,7 @@ public class SSAOUI {
     }
 
     private void init(InputManager inputManager) {
-        System.out.println("----------------- Water UI Debugger --------------------");
+        System.out.println("----------------- SSAO UI Debugger --------------------");
         System.out.println("-- Sample Radius : press Y to increase, H to decrease");
         System.out.println("-- AO Intensity : press U to increase, J to decrease");
         System.out.println("-- AO scale : press I to increase, K to decrease");

+ 6 - 0
jme3-examples/src/main/java/jme3test/post/TestMultiRenderTarget.java

@@ -41,6 +41,7 @@ import com.jme3.math.Matrix4f;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector3f;
 import com.jme3.post.SceneProcessor;
+import com.jme3.profile.AppProfiler;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.queue.RenderQueue;
@@ -231,4 +232,9 @@ public class TestMultiRenderTarget extends SimpleApplication implements ScenePro
     public void cleanup() {
     }
 
+    @Override
+    public void setProfiler(AppProfiler profiler) {
+
+    }
+
 }

+ 6 - 0
jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java

@@ -39,6 +39,7 @@ import com.jme3.math.FastMath;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector3f;
 import com.jme3.post.SceneProcessor;
+import com.jme3.profile.AppProfiler;
 import com.jme3.renderer.Camera;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
@@ -256,5 +257,10 @@ public class TestRenderToMemory extends SimpleApplication implements SceneProces
     public void cleanup() {
     }
 
+    @Override
+    public void setProfiler(AppProfiler profiler) {
+
+    }
+
 
 }

+ 12 - 3
jme3-examples/src/main/java/jme3test/post/TestSSAO2.java

@@ -36,11 +36,10 @@ import com.jme3.light.*;
 import com.jme3.material.Material;
 import com.jme3.math.*;
 import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.DetailedProfilerState;
 import com.jme3.post.ssao.SSAOFilter;
 import com.jme3.scene.*;
-import com.jme3.scene.control.LodControl;
 import com.jme3.scene.shape.Box;
-import com.jme3.texture.Texture;
 
 public class TestSSAO2 extends SimpleApplication {
 
@@ -57,6 +56,11 @@ public class TestSSAO2 extends SimpleApplication {
         dl.setDirection(new Vector3f(-1,-1,-1).normalizeLocal());
         rootNode.addLight(dl);
 
+        flyCam.setDragToRotate(true);
+        setPauseOnLostFocus(false);
+
+        getStateManager().attach(new DetailedProfilerState());
+
         Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
         mat.setFloat("Shininess", 16f);
         //mat.setBoolean("VertexLighting", true);
@@ -96,7 +100,12 @@ public class TestSSAO2 extends SimpleApplication {
 
         FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
         SSAOFilter ssaoFilter = new SSAOFilter(2.9299974f,25f,5.8100376f,0.091000035f);
-        ssaoFilter.setApproximateNormals(true);
+        int numSamples = context.getSettings().getSamples();
+        if (numSamples > 0) {
+            fpp.setNumSamples(numSamples);
+        }
+
+        //ssaoFilter.setApproximateNormals(true);
         fpp.addFilter(ssaoFilter);
         SSAOUI ui = new SSAOUI(inputManager, ssaoFilter);
 

+ 6 - 3
jme3-examples/src/main/java/jme3test/stress/TestShaderNodesStress.java

@@ -6,9 +6,7 @@ import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector3f;
-import com.jme3.profile.AppProfiler;
-import com.jme3.profile.AppStep;
-import com.jme3.profile.VpStep;
+import com.jme3.profile.*;
 import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.queue.RenderQueue;
 import com.jme3.scene.Geometry;
@@ -98,5 +96,10 @@ public class TestShaderNodesStress extends SimpleApplication {
 
         }
 
+        @Override
+        public void spStep(SpStep step, String... additionalInfo) {
+
+        }
+
     }
 }