Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add frametime and TPS graphs #739

Merged
merged 16 commits into from
Nov 20, 2024
Merged
2 changes: 1 addition & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
* [Amazing Trophies 1.2.1 and below](https://github.com/GTNewHorizons/Amazing-Trophies/releases) - use 1.2.2+
* [LWJGL3ify 1.5.4 and below](https://github.com/GTNewHorizons/lwjgl3ify/releases/) - Use the latest version available, 1.5.7+
* [ArchaicFix 0.6.2 and below](https://www.curseforge.com/minecraft/mc-mods/archaicfix/files/all?page=1&pageSize=20) - Use 0.7.0 or above
* [Hodgepodge 2.4.3 and below](https://github.com/GTNewHorizons/Hodgepodge/releases) - Use 2.4.4 or above
* [Hodgepodge 2.4.3 and below](https://github.com/GTNewHorizons/Hodgepodge/releases) - Use 0.4.5 or above
* CodeChicken[Core](https://github.com/GTNewHorizons/CodeChickenCore/releases)/[Lib](https://github.com/GTNewHorizons/CodeChickenLib/releases) <1.2.0 - Threading issues, use 1.3.0+
* Neodymium - Untested, but unlikely to be compatible. (Chunk Meshing is definitely incompatible, other features might work depending on config)

Expand Down
20 changes: 10 additions & 10 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,19 @@ configurations {
}

dependencies {
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.5.71")
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.5.78")

compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:0.4.0") { transitive = false }

compileOnly("org.projectlombok:lombok:1.18.22") { transitive = false }
annotationProcessor("org.projectlombok:lombok:1.18.22")

// Use modern models+textures to inject QuadProviders
devOnlyNonPublishable('com.github.GTNewHorizons:TX-Loader:1.7.1:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:TX-Loader:1.7.2:dev')

// Iris Shaders
compileOnly('org.jetbrains:annotations:24.0.1')
api("com.github.GTNewHorizons:GTNHLib:0.5.12:dev")
api("com.github.GTNewHorizons:GTNHLib:0.5.20:dev")
shadowImplementation("org.anarres:jcpp:1.4.14") // Apache 2.0
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-4.g6b42bca") {
exclude module: "antlr4" // we only want to shadow the runtime module
Expand All @@ -70,18 +70,18 @@ dependencies {
compileOnly "org.apache.ant:ant:1.8.2"

// Because who doesn't want NEI
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.6.39-GTNH:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.3.7:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.6.45-GTNH:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.3.10:dev')

// Notfine Deps
compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles:1.0.4:dev")
compileOnly("com.github.GTNewHorizons:twilightforest:2.6.34:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:twilightforest:2.6.35:dev") { transitive = false }
compileOnly(rfg.deobf('curse.maven:witchery-69673:2234410'))
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.12.10-GTNH:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:Natura:2.7.3:dev")
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.12.14-GTNH:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:Natura:2.7.5:dev")

compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.6.3:dev")
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.6.4:dev")

compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.1:dev") { transitive = false }
compileOnly(rfg.deobf('maven.modrinth:backhand:1.4.1')) { transitive = false }
Expand All @@ -100,7 +100,7 @@ dependencies {
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))

compileOnly("com.github.GTNewHorizons:HoloInventory:2.4.12-GTNH:dev")
compileOnly("com.github.GTNewHorizons:HoloInventory:2.4.13-GTNH:dev")

runtimeOnlyNonPublishable(rfg.deobf("CoreTweaks:CoreTweaks:0.3.3.2"))

Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 5 additions & 2 deletions gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#

##############################################################################
#
Expand Down Expand Up @@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Expand Down Expand Up @@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
Expand Down
2 changes: 2 additions & 0 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem

@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ pluginManagement {
}

plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.27'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.29'
}
270 changes: 270 additions & 0 deletions src/main/java/com/gtnewhorizons/angelica/debug/F3Graph.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
package com.gtnewhorizons.angelica.debug;

import static org.lwjgl.opengl.GL11.GL_ALPHA_TEST;
import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_CULL_FACE;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.GL_TRIANGLE_STRIP;
import static org.lwjgl.opengl.GL11.GL_VERTEX_ARRAY;
import static org.lwjgl.opengl.GL11.glBlendFunc;
import static org.lwjgl.opengl.GL11.glColor4f;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glDisableClientState;
import static org.lwjgl.opengl.GL11.glDrawArrays;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glEnableClientState;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL20.glDisableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glUniform1;
import static org.lwjgl.opengl.GL20.glUniform1f;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;

import com.gtnewhorizon.gtnhlib.client.renderer.shader.ShaderProgram;
import java.nio.FloatBuffer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.BufferUtils;

public abstract class F3Graph {
private static final int NUM_SAMPLES = 240;
// Two floats (x,y)
private static final int VERT_FLOATS = 2;
private static final int VERT_COUNT = 4;
private static final int BORDER = 1;
private static final int HEIGHT = 60;
private static final int WIDTH = NUM_SAMPLES;
private static final int FONT_COLOR = 0xFFE0E0E0;
// Due to GLSL 120 limitations, it's just easier to use floats
private final FloatBuffer sampleBuf = BufferUtils.createFloatBuffer(NUM_SAMPLES);
private final long[] samples = new long[NUM_SAMPLES]; // CPU-side copy for reads
// Circular buffer holding the last 240 samples, in nanoseconds
private int samplesHead = 0; // one ahead of the position of the last sample
private boolean initialized = false;
private ShaderProgram shader;
private int aPos;
private int uFBWidth;
private int uFBHeight;
private int uScaleFactor;
private int uHeadIdx;
private int uSamples;
private int vertBuf;
private final ResourceLocation texture;
private final float pxPerNs;
private final boolean left;
private long sum;

protected F3Graph(ResourceLocation texture, float pxPerNs, boolean left) {
this.texture = texture;
this.pxPerNs = pxPerNs;
this.left = left;
}

public void putSample(long time) {
// Manage running trackers
sum -= samples[samplesHead];
sum += time;
samples[samplesHead] = time;

sampleBuf.put(samplesHead, (float) time);
samplesHead = (samplesHead + 1) % NUM_SAMPLES;
}

private int getVertX(ScaledResolution sres, int idx) {
if (left) {
return switch (idx) {
case 0, 3 -> WIDTH + BORDER * 2;
case 1, 2 -> 0;
default -> throw new RuntimeException("Tried to get out-of-bounds vertex for graph!");
};
}

final int displayWidth = sres.getScaledWidth();
return switch (idx) {
case 0, 3 -> displayWidth;
case 1, 2 -> displayWidth - WIDTH - BORDER * 2;
default -> throw new RuntimeException("Tried to get out-of-bounds vertex for graph!");
};
}

private int getVertY(ScaledResolution sres, int idx) {
final int displayHeight = sres.getScaledHeight();
return switch (idx) {
case 0, 1 -> displayHeight - HEIGHT - BORDER * 2;
case 2, 3 -> displayHeight;
default -> throw new RuntimeException("Tried to get out-of-bounds vertex for graph!");
};
}

private void init() {
shader = new ShaderProgram(
"angelica",
"shaders/debug_graph.vert.glsl",
"shaders/debug_graph.frag.glsl");
shader.use();

// Register attributes
aPos = shader.getAttribLocation("pos");

// Register uniforms
uFBWidth = shader.getUniformLocation("fbWidth");
uFBHeight = shader.getUniformLocation("fbHeight");
uScaleFactor = shader.getUniformLocation("scaleFactor");
uSamples = shader.getUniformLocation("samples");
uHeadIdx = shader.getUniformLocation("headIdx");
final int uPxPerNs = shader.getUniformLocation("pxPerNs");
final int uLeft = shader.getUniformLocation("left");

// Load vertex buffer
vertBuf = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vertBuf);
final FloatBuffer vertices = BufferUtils.createFloatBuffer(VERT_COUNT * VERT_FLOATS);
// Since we use a triangle strip, we only need 4 vertices. The quad extends to the top of the screen so spikes
// don't get truncated. The max height is replaced in the vert shader, no need to be precise.
vertices.put(new float[]{
BORDER, BORDER,
WIDTH + BORDER, BORDER,
BORDER, Float.MAX_VALUE,
WIDTH + BORDER, Float.MAX_VALUE
});
vertices.rewind();

glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

final Minecraft mc = Minecraft.getMinecraft();
final ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);

// Load initial value for uniforms
glUniform1f(uFBWidth, mc.displayWidth);
glUniform1f(uFBHeight, mc.displayHeight);
glUniform1f(uScaleFactor, sr.getScaleFactor());
glUniform1i(uHeadIdx, samplesHead);
glUniform1(uSamples, sampleBuf);
glUniform1f(uPxPerNs, pxPerNs);
glUniform1i(uLeft, left ? 1 : 0); // this is how you load bool uniforms

ShaderProgram.clear();
}

public void render() {
if (!initialized) {
init();
initialized = true;
}

/*
* We try to copy modern vanilla's tracker.
* It is 240 (FRAME_TIMES) wide by 60 tall, including the 1px borders.
* We instead make it 242x62 including borders to prevent the borders from overlapping the samples.
* The background is ARGB 90505050, the borders FFFFFFFF, and the text FFE0E0E0.
* First the background is rendered, then the samples, then the foreground texture and the text.
* The background is drawn via a 242x62 translucent rect.
* Next, the samples are rendered by a shader on a transparent 240x60 rect.
* Finally, the foreground texture and the text is rendered on top.
* The shader pipeline is only needed for the sample draw.
*/

final Minecraft mc = Minecraft.getMinecraft();
final ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);

// Setup GL state
final Tessellator tess = Tessellator.instance;
glDisable(GL_DEPTH_TEST);
glEnable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// Draw background
glDisable(GL_TEXTURE_2D);
glColor4f(0x50 / 255.0F, 0x50 / 255.0F, 0x50 / 255.0F, 0x90 / 255.0F);
tess.startDrawingQuads();
for (int i = 0; i < 4; ++i) {
tess.addVertex(getVertX(sr, i), getVertY(sr, i), 0);
}
tess.draw();
glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
glEnable(GL_TEXTURE_2D);


// Draw samples
shader.use();

// Load uniforms
glUniform1f(uFBWidth, mc.displayWidth);
glUniform1f(uFBHeight, mc.displayHeight);
glUniform1f(uScaleFactor, sr.getScaleFactor());
glUniform1i(uHeadIdx, samplesHead);
glUniform1(uSamples, sampleBuf);

glBindBuffer(GL_ARRAY_BUFFER, vertBuf);
glEnableVertexAttribArray(aPos);
glVertexAttribPointer(aPos, VERT_FLOATS, GL_FLOAT, false, VERT_FLOATS * 4, 0);
glEnableClientState(GL_VERTEX_ARRAY);

// Left side graphs are inverted, so temporarily disable culling
if (!left) glDisable(GL_CULL_FACE);

glDrawArrays(GL_TRIANGLE_STRIP, 0, VERT_COUNT);

if (!left) glEnable(GL_CULL_FACE);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableVertexAttribArray(aPos);
glBindBuffer(GL_ARRAY_BUFFER, 0);

ShaderProgram.clear();

// Draw foreground

mc.getTextureManager().bindTexture(texture);
tess.startDrawingQuads();
tess.addVertexWithUV(getVertX(sr, 0), getVertY(sr, 0), 0.0D, 1, 0);
tess.addVertexWithUV(getVertX(sr, 1), getVertY(sr, 1), 0.0D, 0, 0);
tess.addVertexWithUV(getVertX(sr, 2), getVertY(sr, 2), 0.0D, 0, 1);
tess.addVertexWithUV(getVertX(sr, 3), getVertY(sr, 3), 0.0D, 1, 1);
tess.draw();

// Reset GL state
glEnable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);

// Draw text
// Ensure running counters are reset on first sample
long min = Long.MAX_VALUE;
long max = Long.MIN_VALUE;
for (int i = 0; i < NUM_SAMPLES; ++i) {
min = Math.min(min, samples[i]);
max = Math.max(max, samples[i]);
}
String minStr = String.format("%.1f ms min", min / 1_000_000D);
String avgStr = String.format("%.1f ms avg", sum / 1_000_000 / (double) NUM_SAMPLES);
String maxStr = String.format("%.1f ms max", max / 1_000_000D);
final FontRenderer fr = mc.fontRenderer;
final int top = sr.getScaledHeight() - HEIGHT - BORDER * 2 - fr.FONT_HEIGHT;
final int scaledWidth = sr.getScaledWidth();

if (left) {
fr.drawString(minStr, BORDER * 2, top, FONT_COLOR, true);
fr.drawString(avgStr, BORDER + WIDTH / 2 - fr.getStringWidth(avgStr) / 2, top, FONT_COLOR, true);
fr.drawString(maxStr, BORDER * 2 + WIDTH - fr.getStringWidth(maxStr), top, FONT_COLOR, true);
} else {
fr.drawString(minStr, scaledWidth - (BORDER * 2 + WIDTH), top, FONT_COLOR, true);
fr.drawString(avgStr, scaledWidth - (BORDER + WIDTH / 2 + fr.getStringWidth(avgStr) / 2), top, FONT_COLOR, true);
fr.drawString(maxStr, scaledWidth - (BORDER * 2 + fr.getStringWidth(maxStr)), top, FONT_COLOR, true);
}
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/gtnewhorizons/angelica/debug/FrametimeGraph.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.gtnewhorizons.angelica.debug;

import net.minecraft.util.ResourceLocation;

public class FrametimeGraph extends F3Graph {
public FrametimeGraph() {
// At 1x scale, 30 FPS should be 60 px. 30 FPS = 33_333_333ns per frame, 60px/33_333_333ns = 0.0000018f px/ns
super(
new ResourceLocation("angelica:textures/frametimes_fg.png"),
0.0000018f,
true
);
}
}
Loading