diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/BufferHelper.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/BufferHelper.java index b56b05f..e97b8c9 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/BufferHelper.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/BufferHelper.java @@ -1,7 +1,7 @@ package com.noxcrew.noxesium.feature.ui; import com.mojang.blaze3d.systems.RenderSystem; -import com.noxcrew.noxesium.feature.ui.render.DynamicElement; +import com.noxcrew.noxesium.feature.ui.render.Element; import com.noxcrew.noxesium.feature.ui.render.SharedVertexBuffer; import net.minecraft.client.gui.GuiGraphics; import org.lwjgl.opengl.GL14; @@ -27,7 +27,7 @@ public static void bind(GuiGraphics guiGraphics) { GL14.glBlendColor(1f, 1f, 1f, 1f); // Pre-enable the blending state - DynamicElement.DEFAULT_BLEND_STATE.apply(); + Element.DEFAULT_BLEND_STATE.apply(); } /** diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/DynamicElement.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/DynamicElement.java index 644e27c..9f85d1b 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/DynamicElement.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/DynamicElement.java @@ -1,6 +1,9 @@ package com.noxcrew.noxesium.feature.ui.render; import com.noxcrew.noxesium.NoxesiumMod; +import com.noxcrew.noxesium.feature.ui.render.api.PerSecondRepeatingTask; +import com.noxcrew.noxesium.feature.ui.render.buffer.ElementBuffer; +import com.noxcrew.noxesium.feature.ui.render.buffer.SnapshotableElementBuffer; import java.util.ArrayList; import java.util.List; import net.minecraft.client.gui.GuiGraphics; @@ -10,42 +13,35 @@ */ public class DynamicElement extends Element { - private final List buffers = new ArrayList<>(); + private final List buffers = new ArrayList<>(); + private final PerSecondRepeatingTask nextRender = + new PerSecondRepeatingTask(NoxesiumMod.getInstance().getConfig().maxUiFramerate); private boolean canCheck = true; - - private long nextRender = -1; - private long nextCheck = System.nanoTime() + 1000000000; - private int lastFps = 0; - private boolean movementDirection = false; + private boolean hasBufferLayoutChanged = false; private long lastChange = System.currentTimeMillis(); private int matches = 0; /** - * The current fps at which we re-render the UI elements. + * Returns the repeating task used for rendering. */ - private double renderFps = NoxesiumMod.getInstance().getConfig().maxUiFramerate; + public PerSecondRepeatingTask getRenderTask() { + return nextRender; + } /** * Resets the display fps back to the maximum. */ - private void resetToMax() { - if (!movementDirection) return; + public void resetToMax() { + nextRender.changeFrequency(NoxesiumMod.getInstance().getConfig().maxUiFramerate); - renderFps = NoxesiumMod.getInstance().getConfig().maxUiFramerate; + if (!movementDirection) return; movementDirection = false; matches = 0; lastChange = System.currentTimeMillis(); } - /** - * The current frame rate of this group. - */ - public int renderFramerate() { - return (int) Math.floor(renderFps); - } - /** * Returns the percentage of matching frames. */ @@ -60,15 +56,6 @@ public int buffers() { return buffers.size(); } - /** - * Returns the true frame rate, the amount - * of times this element was rendered the last - * second. - */ - public int framerate() { - return lastFps; - } - /** * Returns whether this element is ready to be considered * for group merging/joining. @@ -77,7 +64,7 @@ public boolean isReady() { if (needsRedraw()) return false; if (isNotEmpty()) { for (var buffer : buffers) { - if (buffer instanceof SnapshotElementBuffer pboBuffer && !pboBuffer.hasValidPBO()) { + if (!buffer.hasValidPBO()) { return false; } } @@ -92,15 +79,6 @@ public void requestCheck() { canCheck = true; } - /** - * Triggers an update of the render framerate. - */ - public void updateRenderFramerate() { - // Just set the render fps back to the max framerate - // whenever the maximum framerate has changed. - renderFps = NoxesiumMod.getInstance().getConfig().maxUiFramerate; - } - /** * Returns whether this element is often changing. Used to determine * when it should be split up this buffer. @@ -125,10 +103,11 @@ public boolean isMergeable() { public void tick() { // Determine if all buffers are the same, // return the entire method if any buffer is not ready. - var verdict = !hasChangedLayers; + var verdict = !hasBufferLayoutChanged; + hasBufferLayoutChanged = false; if (isNotEmpty()) { for (var buffer : buffers) { - if (buffer instanceof SnapshotElementBuffer pboBuffer) { + if (buffer instanceof SnapshotableElementBuffer pboBuffer) { // Process the snapshots var snapshots = pboBuffer.snapshots(); if (snapshots == null) return; @@ -149,12 +128,12 @@ public void tick() { if (movementDirection) { var max = NoxesiumMod.getInstance().getConfig().maxUiFramerate; - renderFps = Math.clamp( + nextRender.changeFrequency(Math.clamp( Math.max( max * (1.0 - ((double) (System.currentTimeMillis() - lastChange) / 10000d)), max * ((double) (60 - matches) / 10d)), 0, - max); + max)); // If matches falls too far if (matches <= 40) { @@ -172,18 +151,18 @@ public void tick() { // Request new PBOs from all buffers if (isNotEmpty()) { for (var buffer : buffers) { - if (buffer instanceof SnapshotElementBuffer pboBuffer) { - pboBuffer.requestNewPBO(); - } + buffer.requestNewPBO(); } } } - /** Tries to make a snapshot of the current buffer. */ + /** + * Tries to make a snapshot of the current buffer. + */ private void trySnapshot() { if (elementsWereDrawn && canCheck) { var target = getTargetBuffer(); - if (target instanceof SnapshotElementBuffer pboBuffer && pboBuffer.canSnapshot()) { + if (target instanceof SnapshotableElementBuffer pboBuffer) { pboBuffer.snapshot(); } } @@ -191,27 +170,13 @@ private void trySnapshot() { @Override public boolean update(long nanoTime, GuiGraphics guiGraphics, Runnable draw) { - // Always start by awaiting the GPU fence + // Always start by awaiting the GPU fence and updating if the PBO is ready + // for the next tick! for (var buffer : buffers) { - if (buffer instanceof SnapshotElementBuffer pboBuffer) { - pboBuffer.awaitFence(); - } - } - - // Initialize the value if it's missing - if (nextRender == -1) { - nextRender = nanoTime; + buffer.awaitFence(); } - // Skip the update until we reach the next render time - if (!needsRedraw && !canCheck && (renderFps <= 20 || nextRender > nanoTime)) return false; - needsRedraw = false; - - // Set the next render time - nextRender = nanoTime + (long) Math.floor(((1 / renderFps) * 1000000000)); - - var result = super.update(nanoTime, guiGraphics, draw); - if (result) { + if (super.update(nanoTime, guiGraphics, draw)) { // Unset check once we're done with this frame, but // snapshot first! if (canCheck) { @@ -223,6 +188,13 @@ public boolean update(long nanoTime, GuiGraphics guiGraphics, Runnable draw) { return false; } + @Override + public boolean shouldRedraw(long nanoTime) { + // If we can check we still increase the next render frame, + // but we ignore its result! + return nextRender.canInvoke(nanoTime) || canCheck; + } + @Override public List getBuffers() { return (List) (List) buffers; @@ -236,8 +208,15 @@ protected void onBufferUntargeted(ElementBuffer buffer) { trySnapshot(); } + @Override + protected void onBufferRemoved(ElementBuffer buffer) { + super.onBufferRemoved(buffer); + hasBufferLayoutChanged = true; + } + @Override public ElementBuffer createBuffer() { - return new SnapshotElementBuffer(); + hasBufferLayoutChanged = true; + return new SnapshotableElementBuffer(); } } diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/Element.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/Element.java index ec99eb2..3550b64 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/Element.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/Element.java @@ -3,11 +3,13 @@ import com.mojang.blaze3d.platform.GlStateManager; import com.noxcrew.noxesium.feature.ui.render.api.BlendState; import com.noxcrew.noxesium.feature.ui.render.api.BlendStateHook; -import com.noxcrew.noxesium.feature.ui.render.api.BufferData; +import com.noxcrew.noxesium.feature.ui.render.api.PerSecondTrackedValue; +import com.noxcrew.noxesium.feature.ui.render.buffer.BufferData; +import com.noxcrew.noxesium.feature.ui.render.buffer.ElementBuffer; import java.io.Closeable; import java.util.List; -import javax.annotation.Nullable; import net.minecraft.client.gui.GuiGraphics; +import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL14; /** @@ -28,8 +30,15 @@ public abstract class Element implements Closeable, BlendStateHook { private boolean lastBlending = true; private int boundBufferIndex = -1; - private final PerSecondTrackedValue updates = new PerSecondTrackedValue(); - private final PerSecondTrackedValue draws = new PerSecondTrackedValue(); + /** + * The amount of updates that occurred in the last second. + */ + public final PerSecondTrackedValue updates = new PerSecondTrackedValue(); + + /** + * The amount of draws that occurred in the last second. + */ + public final PerSecondTrackedValue draws = new PerSecondTrackedValue(); /** * Returns all buffers in this element. @@ -118,7 +127,7 @@ public void submitTextureIds(List buffers) { */ public boolean update(long nanoTime, GuiGraphics guiGraphics, Runnable draw) { this.updates.increment(); - if (!shouldRedraw(nanoTime)) return false; + if (!needsRedraw && !shouldRedraw(nanoTime)) return false; // Bind the first buffer, abort is something goes wrong this.guiGraphics = guiGraphics; @@ -149,7 +158,8 @@ public boolean update(long nanoTime, GuiGraphics guiGraphics, Runnable draw) { var buffers = getBuffers(); for (var index = Math.max(1, this.boundBufferIndex + 1); index < buffers.size(); index++) { - buffers.remove(index).close(); + var oldBuffer = buffers.remove(index); + onBufferRemoved(oldBuffer); } return true; } @@ -207,6 +217,13 @@ protected ElementBuffer getNextLayerBuffer() { return newBuffer; } + /** + * Called when [buffer] is removed from the buffer list. + */ + protected void onBufferRemoved(ElementBuffer buffer) { + buffer.close(); + } + /** * Called before [buffer] stops being the target buffer. */ diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/ElementBufferGroup.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/LayerGroup.java similarity index 83% rename from common/src/main/java/com/noxcrew/noxesium/feature/ui/render/ElementBufferGroup.java rename to common/src/main/java/com/noxcrew/noxesium/feature/ui/render/LayerGroup.java index 3658e70..121ba43 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/ElementBufferGroup.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/LayerGroup.java @@ -13,9 +13,9 @@ import net.minecraft.client.gui.LayeredDraw; /** - * Holds a group of layers and the buffer they are rendering into. + * Holds a group of layers and the dynamic buffer they are rendering into. */ -public class ElementBufferGroup implements Closeable { +public class LayerGroup implements Closeable { private final DynamicElement dynamic = new DynamicElement(); private final List layers = new ArrayList<>(); @@ -60,7 +60,7 @@ public boolean shouldSplit() { /** * Returns whether this group can merge with another. */ - public boolean canMerge(ElementBufferGroup other) { + public boolean canMerge(LayerGroup other) { // If either needs a redraw we don't edit them as // things might be inaccurate! if (!dynamic.isReady() || !other.dynamic.isReady()) return false; @@ -72,7 +72,9 @@ public boolean canMerge(ElementBufferGroup other) { if (!dynamic.isMergeable() || !other.dynamic.isMergeable()) return false; // Don't allow merging when render fps is too different - return Math.abs(dynamic.renderFramerate() - other.dynamic.renderFramerate()) < 10; + return Math.abs(dynamic.getRenderTask().getFramerate() + - other.dynamic.getRenderTask().getFramerate()) + < 10; } /** @@ -86,13 +88,13 @@ public int size() { * Splits up this group into multiple, returns the * new group and edits this group. */ - public ElementBufferGroup split() { + public LayerGroup split() { var total = size(); if (total < 2) throw new IllegalArgumentException("Cannot split up an un-splittable group"); var half = (int) Math.ceil(((double) total) / 2.0); var toSplit = new ArrayList<>(layers.subList(half, total)); removeLayers(toSplit); - var newGroup = new ElementBufferGroup(); + var newGroup = new LayerGroup(); newGroup.addLayers(toSplit); return newGroup; } @@ -100,7 +102,7 @@ public ElementBufferGroup split() { /** * Merges another buffer group into this one. */ - public void join(ElementBufferGroup other) { + public void join(LayerGroup other) { addLayers(other.layers); } @@ -126,20 +128,6 @@ public String layerNames() { .collect(Collectors.joining("/")); } - /** - * Indicates that a check should run the very next frame. - */ - public void requestCheck() { - dynamic.requestCheck(); - } - - /** - * Triggers an update of the render framerate. - */ - public void updateRenderFramerate() { - dynamic.updateRenderFramerate(); - } - @Override public void close() { dynamic.close(); diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/NoxesiumUiRenderState.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/NoxesiumUiRenderState.java index 5e8c278..10dd8ee 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/NoxesiumUiRenderState.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/NoxesiumUiRenderState.java @@ -3,8 +3,8 @@ import com.noxcrew.noxesium.NoxesiumMod; import com.noxcrew.noxesium.feature.ui.BufferHelper; import com.noxcrew.noxesium.feature.ui.layer.NoxesiumLayeredDraw; -import com.noxcrew.noxesium.feature.ui.render.api.BufferData; import com.noxcrew.noxesium.feature.ui.render.api.NoxesiumRenderState; +import com.noxcrew.noxesium.feature.ui.render.buffer.BufferData; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -15,9 +15,9 @@ /** * Stores the entire render state of the current UI. */ -public class NoxesiumUiRenderState implements NoxesiumRenderState { +public class NoxesiumUiRenderState extends NoxesiumRenderState { - private final List groups = new CopyOnWriteArrayList<>(); + private final List groups = new CopyOnWriteArrayList<>(); private final Random random = new Random(); private long nextUpdate = -1; private int lastSize = 0; @@ -25,7 +25,7 @@ public class NoxesiumUiRenderState implements NoxesiumRenderState { /** * Returns all groups in this render state. */ - public List groups() { + public List groups() { return groups; } @@ -84,6 +84,7 @@ public boolean render(GuiGraphics guiGraphics, DeltaTracker deltaTracker, Noxesi group.dynamic().submitTextureIds(ids); } SharedVertexBuffer.draw(ids); + renders.increment(); return true; } @@ -104,12 +105,12 @@ private void updateGroups(NoxesiumLayeredDraw layeredDraw, long nanoTime, boolea if (dynamic) { var chunked = chunked(flattened, flattened.size() / 4); for (var chunk : chunked) { - var group = new ElementBufferGroup(); + var group = new LayerGroup(); group.addLayers(chunk); groups.add(group); } } else { - var group = new ElementBufferGroup(); + var group = new LayerGroup(); group.addLayers(flattened); groups.add(group); } @@ -167,14 +168,14 @@ private void resetGroups() { @Override public void requestCheck() { for (var group : groups) { - group.requestCheck(); + group.dynamic().requestCheck(); } } @Override public void updateRenderFramerate() { for (var group : groups) { - group.updateRenderFramerate(); + group.dynamic().resetToMax(); } } diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/SharedVertexBuffer.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/SharedVertexBuffer.java index 222e860..c8c66ec 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/SharedVertexBuffer.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/SharedVertexBuffer.java @@ -9,7 +9,7 @@ import com.noxcrew.noxesium.feature.CustomCoreShaders; import com.noxcrew.noxesium.feature.ui.render.api.BlendState; import com.noxcrew.noxesium.feature.ui.render.api.BlendStateHook; -import com.noxcrew.noxesium.feature.ui.render.api.BufferData; +import com.noxcrew.noxesium.feature.ui.render.buffer.BufferData; import java.io.Closeable; import java.util.List; import java.util.Objects; diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/StaticBuffer.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/StaticBuffer.java index 744bcdb..1915498 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/StaticBuffer.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/StaticBuffer.java @@ -1,5 +1,8 @@ package com.noxcrew.noxesium.feature.ui.render; +import com.noxcrew.noxesium.NoxesiumMod; +import com.noxcrew.noxesium.feature.ui.render.api.PerSecondRepeatingTask; +import com.noxcrew.noxesium.feature.ui.render.buffer.ElementBuffer; import java.util.ArrayList; import java.util.List; @@ -10,6 +13,8 @@ public class StaticBuffer extends Element { private final List buffers = new ArrayList<>(); + private final PerSecondRepeatingTask nextRender = + new PerSecondRepeatingTask(NoxesiumMod.getInstance().getConfig().maxUiFramerate); @Override public List getBuffers() { @@ -23,6 +28,6 @@ public ElementBuffer createBuffer() { @Override public boolean shouldRedraw(long nanoTime) { - return true; + return nextRender.canInvoke(nanoTime); } } diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/NoxesiumRenderState.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/NoxesiumRenderState.java index 705271e..512a124 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/NoxesiumRenderState.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/NoxesiumRenderState.java @@ -5,20 +5,25 @@ /** * The basis for a render state object. */ -public interface NoxesiumRenderState extends Closeable { +public abstract class NoxesiumRenderState implements Closeable { + + /** + * The amount of screen renders that occurred in the last second. + */ + public final PerSecondTrackedValue renders = new PerSecondTrackedValue(); /** * Ticks this render state. */ - void tick(); + public abstract void tick(); /** * Indicates that a check should run the very next frame. */ - void requestCheck(); + public abstract void requestCheck(); /** * Triggers an update of the render framerate. */ - void updateRenderFramerate(); + public abstract void updateRenderFramerate(); } diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/PerSecondRepeatingTask.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/PerSecondRepeatingTask.java new file mode 100644 index 0000000..09ac144 --- /dev/null +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/PerSecondRepeatingTask.java @@ -0,0 +1,71 @@ +package com.noxcrew.noxesium.feature.ui.render.api; + +/** + * A helper to track if a task should be executed yet when it wants + * to reach some goal per second. + */ +public class PerSecondRepeatingTask { + + private double frequency; + private long nextInvocation; + private long nanosPerInvocation; + + public PerSecondRepeatingTask(double frequency) { + this(frequency, System.nanoTime()); + } + + public PerSecondRepeatingTask(double frequency, long nanoTime) { + changeFrequency(frequency, nanoTime); + if (canInvoke(nanoTime)) { + nextInvocation -= nanosPerInvocation; + } + } + + /** + * Returns the rounded framerate of this task. + */ + public int getFramerate() { + return (int) Math.floor(frequency); + } + + /** + * Returns whether this task can currently be invoked. + */ + public boolean canInvoke() { + return canInvoke(System.nanoTime()); + } + + /** + * Returns whether this task can currently be invoked. + */ + public boolean canInvoke(long nanoTime) { + if (nanoTime > nextInvocation) { + nextInvocation = (nanoTime / nanosPerInvocation * nanosPerInvocation) + nanosPerInvocation; + return true; + } + return false; + } + + /** + * Changes the frequency of this task. + */ + public void changeFrequency(double value) { + changeFrequency(value, System.nanoTime()); + } + + /** + * Changes the frequency of this task. + */ + public void changeFrequency(double value, long nanoTime) { + this.frequency = value; + this.nanosPerInvocation = (long) Math.floor(((1 / value) * 1000000000)); + this.nextInvocation = (nanoTime / nanosPerInvocation * nanosPerInvocation) + nanosPerInvocation; + } + + /** + * Returns how frequently this task runs per second. + */ + public double getFrequency() { + return frequency; + } +} diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/PerSecondTrackedValue.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/PerSecondTrackedValue.java similarity index 94% rename from common/src/main/java/com/noxcrew/noxesium/feature/ui/render/PerSecondTrackedValue.java rename to common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/PerSecondTrackedValue.java index ee44235..005cd55 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/PerSecondTrackedValue.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/PerSecondTrackedValue.java @@ -1,4 +1,4 @@ -package com.noxcrew.noxesium.feature.ui.render; +package com.noxcrew.noxesium.feature.ui.render.api; /** * A value that we want to track every second. diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/BufferData.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/buffer/BufferData.java similarity index 52% rename from common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/BufferData.java rename to common/src/main/java/com/noxcrew/noxesium/feature/ui/render/buffer/BufferData.java index c2adfca..3b5cdfb 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/BufferData.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/buffer/BufferData.java @@ -1,4 +1,6 @@ -package com.noxcrew.noxesium.feature.ui.render.api; +package com.noxcrew.noxesium.feature.ui.render.buffer; + +import com.noxcrew.noxesium.feature.ui.render.api.BlendState; /** * Stores a buffer's texture and its desired blend state. diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/ElementBuffer.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/buffer/ElementBuffer.java similarity index 96% rename from common/src/main/java/com/noxcrew/noxesium/feature/ui/render/ElementBuffer.java rename to common/src/main/java/com/noxcrew/noxesium/feature/ui/render/buffer/ElementBuffer.java index 59ce986..390e42d 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/ElementBuffer.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/buffer/ElementBuffer.java @@ -1,10 +1,11 @@ -package com.noxcrew.noxesium.feature.ui.render; +package com.noxcrew.noxesium.feature.ui.render.buffer; import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.pipeline.TextureTarget; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.noxcrew.noxesium.feature.ui.BufferHelper; +import com.noxcrew.noxesium.feature.ui.render.SharedVertexBuffer; import com.noxcrew.noxesium.feature.ui.render.api.BlendState; import java.io.Closeable; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/SnapshotElementBuffer.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/buffer/SnapshotableElementBuffer.java similarity index 88% rename from common/src/main/java/com/noxcrew/noxesium/feature/ui/render/SnapshotElementBuffer.java rename to common/src/main/java/com/noxcrew/noxesium/feature/ui/render/buffer/SnapshotableElementBuffer.java index 4058099..526381e 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/SnapshotElementBuffer.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/buffer/SnapshotableElementBuffer.java @@ -1,4 +1,4 @@ -package com.noxcrew.noxesium.feature.ui.render; +package com.noxcrew.noxesium.feature.ui.render.buffer; import com.google.common.base.Preconditions; import com.mojang.blaze3d.buffers.BufferType; @@ -17,27 +17,19 @@ * An element buffer that also has an attached PBO * and bound buffer so it can be snapshot. */ -public class SnapshotElementBuffer extends ElementBuffer { +public class SnapshotableElementBuffer extends ElementBuffer { private int currentIndex = 0; private int validPbos = 0; - private boolean pboReady; private GpuBuffer[] pbos; private ByteBuffer[] buffers; private GpuFence fence; - /** - * Returns whether the buffer is ready for a snapshot. - */ - public boolean canSnapshot() { - return !pboReady && fence == null && validPbos < 2; - } - /** * Returns the snapshots that were taken. */ public ByteBuffer[] snapshots() { - return pboReady ? buffers : null; + return validPbos >= 2 ? buffers : null; } /** @@ -51,7 +43,6 @@ public boolean hasValidPBO() { * Marks down that a new PBO should be updated. */ public void requestNewPBO() { - pboReady = false; validPbos--; } @@ -67,7 +58,6 @@ public void awaitFence() { // compared. validPbos++; fence = null; - pboReady = true; } } @@ -75,19 +65,17 @@ public void awaitFence() { * Snapshots the current buffer contents to a PBO. */ public void snapshot() { - if (fence != null || pbos == null || buffers == null) return; - - // Flip which buffer we are drawing into - if (currentIndex == 1) currentIndex = 0; - else currentIndex = 1; + if (fence != null || validPbos >= 2 || pbos == null || buffers == null) return; // Bind the PBO to tell the GPU to read the frame buffer's // texture into it directly pbos[currentIndex].bind(); // TODO This causes micro-stutters! + var start = System.nanoTime(); var window = Minecraft.getInstance().getWindow(); GL11.glReadPixels(0, 0, window.getWidth(), window.getHeight(), GL30.GL_BGRA, GL11.GL_UNSIGNED_BYTE, 0); + System.out.println("glReadPixels took " + (System.nanoTime() - start) + " ns"); // GetTexImage produces weird results sometimes, it doesn't seem to catch // the crosshair changing and thinks the scoreboard changes every tick. @@ -102,6 +90,10 @@ public void snapshot() { // Unbind the PBO so it doesn't get modified afterwards GlStateManager._glBindBuffer(GL30.GL_PIXEL_PACK_BUFFER, 0); + // Flip which buffer we will use next + if (currentIndex == 1) currentIndex = 0; + else currentIndex = 1; + // Start waiting for the GPU to return the data fence = new GpuFence(); } diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/screen/NoxesiumScreenRenderState.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/screen/NoxesiumScreenRenderState.java index f8bed10..b20b223 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/screen/NoxesiumScreenRenderState.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/screen/NoxesiumScreenRenderState.java @@ -3,8 +3,8 @@ import com.noxcrew.noxesium.feature.ui.BufferHelper; import com.noxcrew.noxesium.feature.ui.render.DynamicElement; import com.noxcrew.noxesium.feature.ui.render.SharedVertexBuffer; -import com.noxcrew.noxesium.feature.ui.render.api.BufferData; import com.noxcrew.noxesium.feature.ui.render.api.NoxesiumRenderState; +import com.noxcrew.noxesium.feature.ui.render.buffer.BufferData; import java.util.ArrayList; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.Screen; @@ -12,7 +12,7 @@ /** * Stores the render state for the on-screen UI element. */ -public class NoxesiumScreenRenderState implements NoxesiumRenderState { +public class NoxesiumScreenRenderState extends NoxesiumRenderState { private Screen lastScreen; private final DynamicElement dynamic = new DynamicElement(); @@ -47,6 +47,7 @@ public boolean render(GuiGraphics guiGraphics, int width, int height, float delt var ids = new ArrayList(); dynamic.submitTextureIds(ids); SharedVertexBuffer.draw(ids); + renders.increment(); return true; } @@ -57,7 +58,7 @@ public void requestCheck() { @Override public void updateRenderFramerate() { - dynamic.updateRenderFramerate(); + dynamic.resetToMax(); } @Override diff --git a/common/src/main/java/com/noxcrew/noxesium/mixin/ui/GuiMixin.java b/common/src/main/java/com/noxcrew/noxesium/mixin/ui/GuiMixin.java index 3bacc44..ec05bdc 100644 --- a/common/src/main/java/com/noxcrew/noxesium/mixin/ui/GuiMixin.java +++ b/common/src/main/java/com/noxcrew/noxesium/mixin/ui/GuiMixin.java @@ -6,7 +6,9 @@ import com.noxcrew.noxesium.feature.rule.ServerRules; import com.noxcrew.noxesium.feature.ui.CustomMapUiWidget; import com.noxcrew.noxesium.feature.ui.layer.LayeredDrawExtension; +import com.noxcrew.noxesium.feature.ui.render.DynamicElement; import com.noxcrew.noxesium.feature.ui.render.NoxesiumUiRenderState; +import com.noxcrew.noxesium.feature.ui.render.api.NoxesiumRenderState; import com.noxcrew.noxesium.feature.ui.render.screen.NoxesiumScreenRenderState; import java.util.ArrayList; import java.util.function.Supplier; @@ -91,26 +93,15 @@ public abstract class GuiMixin { switch (stateIn) { case NoxesiumUiRenderState state -> { for (var group : state.groups()) { - var dynamic = group.dynamic(); - var name = group.layerNames(); - if (name.length() >= 100) { - name = name.substring(0, 100); - } - text.add(Component.literal("§b" + name - + (group.dynamic().buffers() > 1 - ? " §3(+" + (group.dynamic().buffers() - 1) + ")" - : "") - + (group.dynamic().isEmpty() ? " §9(empty)" : "") + ": §f" + dynamic.framerate() - + " - " + dynamic.matchRate())); + var name = "§b" + group.layerNames(); + noxesium$addLine(text, name, state, group.dynamic()); } } case NoxesiumScreenRenderState state -> { // Only show this if we are currently running the screen optimizations if (Minecraft.getInstance().screen instanceof MenuAccess || Minecraft.getInstance().screen instanceof ChatScreen) { - var dynamic = state.dynamic(); - text.add(Component.literal( - "§eScreen: §f" + dynamic.framerate() + " - " + dynamic.matchRate())); + noxesium$addLine(text, "§eScreen", state, state.dynamic()); } } case null -> {} @@ -128,6 +119,26 @@ public abstract class GuiMixin { } } + /** + * Adds a line to the list of debugged elements. + */ + @Unique + private void noxesium$addLine( + ArrayList text, String name, NoxesiumRenderState state, DynamicElement dynamic) { + if (name.length() >= 100) { + name = name.substring(0, 100); + } + text.add(Component.literal(String.format( + "%s: §f%d renders, %d updates, %d draws, %s% matches", + name + + (dynamic.buffers() > 1 ? " §3(+" + (dynamic.buffers() - 1) + ")" : "") + + (dynamic.isEmpty() ? " §9(empty)" : ""), + state.renders.get(), + dynamic.updates.get(), + dynamic.draws.get(), + dynamic.matchRate()))); + } + /** * Adds a new rendered element to the UI with the given condition and layer. */