All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.app.DetailedProfiler Maven / Gradle / Ivy

There is a newer version: 3.7.0-stable
Show newest version
/*
 * Copyright (c) 2017-2021 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.app;

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 data;
    private Map 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;

    final private StringBuilder path = new StringBuilder(256);
    final private StringBuilder vpPath = new StringBuilder(256);

    final private Deque 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 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();
        }
    }
    
    
    @Override
    public void appSubStep(String... additionalInfo) {
        if (data != null) {
            String pathStep = getPath("", additionalInfo);
            path.setLength(0);
            path.append(curAppPath).append(pathStep);
            addStep(path.toString(), System.nanoTime());
        }
    }

    private void closeFrame() {
        //close frame
        if (data != null) {
            if (ongoingGpuProfiling && renderer != null) {
                renderer.stopProfiling();
                ongoingGpuProfiling = false;
            }
            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 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 {
        final private long[] cpuTimes = new long[MAX_FRAMES];
        final private long[] gpuTimes = new long[MAX_FRAMES];
        private int startCursor = 0;
        private int cpuCursor = 0;
        private int gpuCursor = 0;
        private long cpuSum = 0;
        private long gpuSum = 0;
        private long lastValue = 0;
        private int nbFramesCpu;
        private int nbFramesGpu;
        List 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 cpuSum / (double) Math.min(nbFramesCpu, MAX_FRAMES);
        }

        public double getAverageGpu() {
            if (nbFramesGpu == 0) {
                return 0;
            }

            return gpuSum / (double) Math.min(nbFramesGpu, MAX_FRAMES);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy