net.guerra24.infinity.client.graphics.nanovg.Timers.java Source code

Java tutorial

Introduction

Here is the source code for net.guerra24.infinity.client.graphics.nanovg.Timers.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015-2016 Guerra24
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.guerra24.infinity.client.graphics.nanovg;

import static net.guerra24.infinity.client.graphics.VectorsRendering.colorA;
import static net.guerra24.infinity.client.graphics.VectorsRendering.rgba;
import static org.lwjgl.glfw.GLFW.glfwGetTime;
import static org.lwjgl.glfw.GLFW.glfwSetTime;
import static org.lwjgl.nanovg.NanoVG.NVG_ALIGN_BOTTOM;
import static org.lwjgl.nanovg.NanoVG.NVG_ALIGN_LEFT;
import static org.lwjgl.nanovg.NanoVG.NVG_ALIGN_RIGHT;
import static org.lwjgl.nanovg.NanoVG.NVG_ALIGN_TOP;
import static org.lwjgl.nanovg.NanoVG.nvgBeginPath;
import static org.lwjgl.nanovg.NanoVG.nvgFill;
import static org.lwjgl.nanovg.NanoVG.nvgFillColor;
import static org.lwjgl.nanovg.NanoVG.nvgFontFace;
import static org.lwjgl.nanovg.NanoVG.nvgFontSize;
import static org.lwjgl.nanovg.NanoVG.nvgLineTo;
import static org.lwjgl.nanovg.NanoVG.nvgMoveTo;
import static org.lwjgl.nanovg.NanoVG.nvgRect;
import static org.lwjgl.nanovg.NanoVG.nvgText;
import static org.lwjgl.nanovg.NanoVG.nvgTextAlign;
import static org.lwjgl.opengl.ARBTimerQuery.GL_TIME_ELAPSED;
import static org.lwjgl.opengl.ARBTimerQuery.glGetQueryObjectui64v;
import static org.lwjgl.opengl.GL15.GL_QUERY_RESULT;
import static org.lwjgl.opengl.GL15.GL_QUERY_RESULT_AVAILABLE;
import static org.lwjgl.opengl.GL15.glBeginQuery;
import static org.lwjgl.opengl.GL15.glEndQuery;
import static org.lwjgl.opengl.GL15.glGenQueries;
import static org.lwjgl.opengl.GL15.glGetQueryObjectiv;
import static org.lwjgl.system.MemoryUtil.NULL;
import static org.lwjgl.system.MemoryUtil.memAllocInt;
import static org.lwjgl.system.MemoryUtil.memAllocLong;
import static org.lwjgl.system.MemoryUtil.memEncodeUTF8;
import static org.lwjgl.system.MemoryUtil.memFree;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.util.Arrays;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL;

import net.guerra24.infinity.client.graphics.opengl.Display;

public class Timers {

    private static long vg;
    private static double t, dt;
    private static GPUtimer gpuTimer;
    private static PerfGraph fps;
    private static PerfGraph cpuGraph;
    private static PerfGraph gpuGraph;
    private static double prevt;
    private static final FloatBuffer gpuTimes = BufferUtils.createFloatBuffer(3);
    static final int GRAPH_RENDER_FPS = 0, GRAPH_RENDER_MS = 1, GRAPH_RENDER_PERCENT = 2;

    private static final int GRAPH_HISTORY_COUNT = 200;
    private static final int GPU_QUERY_COUNT = 5;

    private static Display display;

    public static void setDisplay(Display display) {
        Timers.display = display;
    }

    public static void initDebugDisplay() {
        vg = display.getVg();

        gpuTimer = new GPUtimer();
        fps = new PerfGraph();
        cpuGraph = new PerfGraph();
        gpuGraph = new PerfGraph();

        initGraph(fps, GRAPH_RENDER_FPS, "Frame Time");
        initGraph(cpuGraph, GRAPH_RENDER_MS, "CPU Time");
        initGraph(gpuGraph, GRAPH_RENDER_MS, "GPU Time");
        initGPUTimer(gpuTimer);

        glfwSetTime(0);
        prevt = glfwGetTime();
    }

    public static void renderDebugDisplay(float x, float y, float w, float h) {
        renderGraph(vg, x, y, w, h, fps);
        renderGraph(vg, x + w + 5, y, w, h, cpuGraph);
        if (gpuTimer.supported)
            renderGraph(vg, x + w + 5 + w + 5, y, w, h, gpuGraph);

    }

    private static void updateDebugDisplay() {
        updateGraph(fps, (float) dt);
    }

    public static void startCPUTimer() {
        t = glfwGetTime();
        dt = t - prevt;
        prevt = t;
    }

    public static void stopCPUTimer() {
        double cpuTime = glfwGetTime() - t;
        updateGraph(cpuGraph, (float) cpuTime);
    }

    public static void startGPUTimer() {
        startGPUTimer(gpuTimer);
    }

    public static void stopGPUTimer() {
        int n = stopGPUTimer(gpuTimer, gpuTimes, 3);
        for (int i = 0; i < n; i++)
            updateGraph(gpuGraph, gpuTimes.get(i));
    }

    public static void update() {
        updateDebugDisplay();
    }

    private static class PerfGraph {
        int style;
        ByteBuffer name = BufferUtils.createByteBuffer(32);
        float[] values = new float[GRAPH_HISTORY_COUNT];
        int head;
    }

    static class GPUtimer {
        boolean supported;
        int cur, ret;
        IntBuffer queries = BufferUtils.createIntBuffer(GPU_QUERY_COUNT);
    }

    private static void initGPUTimer(GPUtimer timer) {
        // memset(timer, 0, sizeof(*timer))2
        timer.supported = GL.getCapabilities().GL_ARB_timer_query;
        timer.cur = 0;
        timer.ret = 0;
        BufferUtils.zeroBuffer(timer.queries);

        if (timer.supported)
            glGenQueries(timer.queries);
    }

    private static void startGPUTimer(GPUtimer timer) {
        if (!timer.supported)
            return;
        glBeginQuery(GL_TIME_ELAPSED, timer.queries.get(timer.cur % GPU_QUERY_COUNT));
        timer.cur++;
    }

    private static int stopGPUTimer(GPUtimer timer, FloatBuffer times, int maxTimes) {
        int n = 0;
        if (!timer.supported)
            return 0;

        glEndQuery(GL_TIME_ELAPSED);

        IntBuffer available = memAllocInt(1);
        available.put(0, 1);
        while (available.get(0) != 0 && timer.ret <= timer.cur) {
            // check for results if there are any
            glGetQueryObjectiv(timer.queries.get(timer.ret % GPU_QUERY_COUNT), GL_QUERY_RESULT_AVAILABLE,
                    available);
            if (available.get(0) != 0) {
                LongBuffer timeElapsed = memAllocLong(1);
                glGetQueryObjectui64v(timer.queries.get(timer.ret % GPU_QUERY_COUNT), GL_QUERY_RESULT, timeElapsed);
                timer.ret++;
                if (n < maxTimes) {
                    times.put(n, (float) ((double) timeElapsed.get(0) * 1e-9));
                    n++;
                }
                memFree(timeElapsed);
            }
        }
        memFree(available);
        return n;
    }

    private static void initGraph(PerfGraph fps, int style, String name) {
        fps.style = style;
        fps.name = memEncodeUTF8(name);
        Arrays.fill(fps.values, 0);
        fps.head = 0;
    }

    private static void updateGraph(PerfGraph fps, float frameTime) {
        fps.head = (fps.head + 1) % GRAPH_HISTORY_COUNT;
        fps.values[fps.head] = frameTime;
    }

    private static float getGraphAverage(PerfGraph fps) {
        float avg = 0;
        for (int i = 0; i < GRAPH_HISTORY_COUNT; i++) {
            avg += fps.values[i];
        }
        return avg / (float) GRAPH_HISTORY_COUNT;
    }

    private static void renderGraph(long vg, float x, float y, float w, float h, PerfGraph fps) {
        float avg = getGraphAverage(fps);

        nvgBeginPath(vg);
        nvgRect(vg, x, y, w, h);
        nvgFillColor(vg, rgba(0, 0, 0, 128, colorA));
        nvgFill(vg);

        nvgBeginPath(vg);
        nvgMoveTo(vg, x, y + h);
        if (fps.style == GRAPH_RENDER_FPS) {
            for (int i = 0; i < GRAPH_HISTORY_COUNT; i++) {
                float v = 1.0f / (0.00001f + fps.values[(fps.head + i) % GRAPH_HISTORY_COUNT]);
                float vx, vy;
                if (v > 1000.0f)
                    v = 1000.0f;
                vx = x + ((float) i / (GRAPH_HISTORY_COUNT - 1)) * w;
                vy = y + h - ((v / 1000.0f) * h);
                nvgLineTo(vg, vx, vy);
            }
        } else if (fps.style == GRAPH_RENDER_PERCENT) {
            for (int i = 0; i < GRAPH_HISTORY_COUNT; i++) {
                float v = fps.values[(fps.head + i) % GRAPH_HISTORY_COUNT] * 1.0f;
                float vx, vy;
                if (v > 100.0f)
                    v = 100.0f;
                vx = x + ((float) i / (GRAPH_HISTORY_COUNT - 1)) * w;
                vy = y + h - ((v / 100.0f) * h);
                nvgLineTo(vg, vx, vy);
            }
        } else {
            for (int i = 0; i < GRAPH_HISTORY_COUNT; i++) {
                float v = fps.values[(fps.head + i) % GRAPH_HISTORY_COUNT] * 1000.0f;
                float vx, vy;
                if (v > 4.0f)
                    v = 4.0f;
                vx = x + ((float) i / (GRAPH_HISTORY_COUNT - 1)) * w;
                vy = y + h - ((v / 4.0f) * h);
                nvgLineTo(vg, vx, vy);
            }
        }
        nvgLineTo(vg, x + w, y + h);
        nvgFillColor(vg, rgba(255, 192, 0, 128, colorA));
        nvgFill(vg);

        nvgFontFace(vg, "Roboto-Bold");

        if (fps.name.get(0) != '\0') {
            nvgFontSize(vg, 14.0f);
            nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
            nvgFillColor(vg, rgba(240, 240, 240, 192, colorA));
            nvgText(vg, x + 3, y + 1, fps.name, NULL);
        }

        if (fps.style == GRAPH_RENDER_FPS) {
            nvgFontSize(vg, 18.0f);
            nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP);
            nvgFillColor(vg, rgba(240, 240, 240, 255, colorA));
            nvgText(vg, x + w - 3, y + 1, String.format("%.2f FPS", 1.0f / avg), NULL);

            nvgFontSize(vg, 15.0f);
            nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM);
            nvgFillColor(vg, rgba(240, 240, 240, 160, colorA));
            nvgText(vg, x + w - 3, y + h - 1, String.format("%.2f ms", avg * 1000.0f), NULL);
        } else if (fps.style == GRAPH_RENDER_PERCENT) {
            nvgFontSize(vg, 18.0f);
            nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP);
            nvgFillColor(vg, rgba(240, 240, 240, 255, colorA));
            nvgText(vg, x + w - 3, y + 1, String.format("%.1f %%", avg * 1.0f), NULL);
        } else {
            nvgFontSize(vg, 18.0f);
            nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP);
            nvgFillColor(vg, rgba(240, 240, 240, 255, colorA));
            nvgText(vg, x + w - 3, y + 1, String.format("%.2f ms", avg * 1000.0f), NULL);
        }
    }

}