From a1dfa925170fb33dc6f56bc4fca2a623ecd999b5 Mon Sep 17 00:00:00 2001 From: Aeltumn Date: Thu, 19 Dec 2024 00:49:47 +0100 Subject: [PATCH] Split out experimental patches into two separate optimizations --- README.md | 2 +- .../api/protocol/rule/ServerRuleIndices.java | 7 +- .../com/noxcrew/noxesium/NoxesiumMod.java | 43 ++--- .../noxesium/config/NoxesiumConfig.java | 33 +--- .../noxesium/config/NoxesiumOptions.java | 46 +++-- .../config/NoxesiumSettingsScreen.java | 3 +- .../entity/SpatialInteractionEntityTree.java | 4 +- .../noxesium/feature/rule/ServerRules.java | 5 - .../feature/ui/LayerWithReference.java | 4 +- .../feature/ui/layer/NoxesiumLayer.java | 7 - .../feature/ui/layer/NoxesiumLayeredDraw.java | 64 +++---- .../feature/ui/render/ElementBuffer.java | 3 +- .../feature/ui/render/ElementBufferGroup.java | 11 -- .../ui/render/NoxesiumUiRenderState.java | 166 ++++++++++-------- .../feature/ui/render/SharedVertexBuffer.java | 12 +- .../render/api/NoxesiumRenderStateHolder.java | 4 + .../screen/NoxesiumScreenRenderState.java | 8 +- .../render/screen/ScreenRenderingHolder.java | 4 +- .../mixin/ui/CustomDebugHotkeysMixin.java | 25 +-- .../noxcrew/noxesium/mixin/ui/GuiMixin.java | 16 +- .../noxesium/mixin/ui/LayeredDrawMixin.java | 25 ++- .../mixin/ui/ScreenRenderHookMixin.java | 15 +- .../resources/assets/noxesium/lang/en_us.json | 26 +-- 23 files changed, 224 insertions(+), 309 deletions(-) diff --git a/README.md b/README.md index 0740d023..419e1deb 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ There are also a few improvements that do not require a server: ### Performance -Noxesium contains a performance patch which reworks UI rendering to have a dynamic fps. Instead of rendering the UI every frame it gets rendered between 20 and 60 times a second. Whenever a UI element changes it starts getting rendered at 60 fps, while UI elements that rarely change render at 20 fps. These are currently experimental and have to manually be enabled in the Noxesium configuration screen. +Noxesium contains an optional performance patch which limits UI rendering to be limited to 60 fps which can improve fps at high values. There is an additional setting available to enable dynamic UI fps lowering, going down to 20 fps. ### Bugfixes diff --git a/api/src/main/java/com/noxcrew/noxesium/api/protocol/rule/ServerRuleIndices.java b/api/src/main/java/com/noxcrew/noxesium/api/protocol/rule/ServerRuleIndices.java index 6b07aea7..20c7dcba 100644 --- a/api/src/main/java/com/noxcrew/noxesium/api/protocol/rule/ServerRuleIndices.java +++ b/api/src/main/java/com/noxcrew/noxesium/api/protocol/rule/ServerRuleIndices.java @@ -42,12 +42,7 @@ public class ServerRuleIndices { */ public static final int HAND_ITEM_OVERRIDE = 8; - /** - * Disables the UI optimizations provided by Noxesium. There are currently - * no known incompatibilities (text shaders are fully supported), but this - * option is still provided in case it becomes necessary. - */ - public static final int DISABLE_UI_OPTIMIZATIONS = 9; + // Id 9 has been removed. /** * Moves the handheld map to be shown in the top left/right corner instead of diff --git a/common/src/main/java/com/noxcrew/noxesium/NoxesiumMod.java b/common/src/main/java/com/noxcrew/noxesium/NoxesiumMod.java index 5478e19a..e9f319c1 100644 --- a/common/src/main/java/com/noxcrew/noxesium/NoxesiumMod.java +++ b/common/src/main/java/com/noxcrew/noxesium/NoxesiumMod.java @@ -115,48 +115,33 @@ public NoxesiumMod(NoxesiumPlatformHook platformHook) { ignored = CustomCoreShaders.BLIT_SCREEN_MULTIPLE; ignored = CustomRenderTypes.linesNoDepth(); - // Run rebuilds on a separate thread to not destroy fps unnecessarily - var rebuildThread = new Thread("Noxesium Spatial Container Rebuild Thread") { + // Run rebuilds on a separate thread to not destroy fps unnecessarily, also run + // frame comparisons on this thread if they are enabled. + var backgroundTaskThread = new Thread("Noxesium Background Task Thread") { @Override public void run() { while (true) { try { - Thread.sleep(2500); SpatialInteractionEntityTree.rebuild(); - } catch (InterruptedException ex) { - return; - } catch (Exception ex) { - logger.error("Caught exception from Noxesium Spatial Container Rebuild Thread", ex); - } - } - } - }; - rebuildThread.setDaemon(true); - rebuildThread.start(); - - // Also run frame comparisons on another thread - var frameComparisonThread = new Thread("Noxesium Frame Comparison Thread") { - @Override - public void run() { - while (true) { - try { - forEachRenderStateHolder((it) -> { - var state = it.get(); - if (state != null) { - state.tick(); - } - }); + if (getConfig().enableDynamicUiLimiting) { + forEachRenderStateHolder((it) -> { + var state = it.get(); + if (state != null) { + state.tick(); + } + }); + } Thread.sleep(20); } catch (InterruptedException ex) { return; } catch (Exception ex) { - logger.error("Caught exception from Noxesium Frame Comparison Thread", ex); + logger.error("Caught exception from Noxesium Background Task Thread", ex); } } } }; - frameComparisonThread.setDaemon(true); - frameComparisonThread.start(); + backgroundTaskThread.setDaemon(true); + backgroundTaskThread.start(); } /** diff --git a/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumConfig.java b/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumConfig.java index 2019877f..276b9d52 100644 --- a/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumConfig.java +++ b/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumConfig.java @@ -16,12 +16,6 @@ */ public class NoxesiumConfig { - /** - * The current state of the hotkey for toggling the experimental - * patches on/off. - */ - public static Boolean experimentalPatchesHotkey = null; - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); public boolean resetToggleKeys = false; @@ -29,22 +23,16 @@ public class NoxesiumConfig { public boolean showFpsOverlay = false; public boolean showGameTimeOverlay = false; public boolean enableQibSystemDebugging = false; - public boolean disableExperimentalPerformancePatches = true; // Still off by default until thoroughly tested! public boolean showGlowingSettings = false; public boolean dumpIncomingPackets = false; public boolean dumpOutgoingPackets = false; public boolean printPacketExceptions = false; public double mapUiSize = 0.8; public MapLocation mapUiLocation = MapLocation.TOP; + public boolean enableUiLimiting = false; + public boolean enableDynamicUiLimiting = false; + public boolean showUiDebugOverlay = false; public int maxUiFramerate = 60; - public boolean showOptimizationOverlay = false; - - /** - * Returns whether experimental performance are enabled in the configuration. - */ - public boolean hasConfiguredPerformancePatches() { - return !disableExperimentalPerformancePatches; - } /** * Returns whether to render maps in the UI. @@ -56,21 +44,6 @@ public boolean shouldRenderMapsInUi() { return renderMapsInUi == BooleanOrDefault.TRUE; } - /** - * Whether the experimental performance patches should be used. - */ - public boolean shouldDisableExperimentalPerformancePatches() { - if (ServerRules.DISABLE_UI_OPTIMIZATIONS.getValue()) return true; - - if (hasConfiguredPerformancePatches()) { - if (experimentalPatchesHotkey != null) { - return !experimentalPatchesHotkey; - } - return false; - } - return true; - } - /** * Loads this configuration file. */ diff --git a/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumOptions.java b/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumOptions.java index db2ca524..f2c48fdd 100644 --- a/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumOptions.java +++ b/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumOptions.java @@ -13,16 +13,6 @@ */ public class NoxesiumOptions { - private static final OptionInstance experimentalPatches = OptionInstance.createBoolean( - "noxesium.options.experimental_patches.name", - OptionInstance.cachedConstantTooltip(Component.translatable("noxesium.options.experimental_patches.tooltip.v2")), - NoxesiumMod.getInstance().getConfig().hasConfiguredPerformancePatches(), - (newValue) -> { - NoxesiumMod.getInstance().getConfig().disableExperimentalPerformancePatches = !newValue; - NoxesiumMod.getInstance().getConfig().save(); - } - ); - private static final OptionInstance fpsOverlay = OptionInstance.createBoolean( "noxesium.options.fps_overlay.name", OptionInstance.cachedConstantTooltip(Component.translatable("noxesium.options.fps_overlay.tooltip")), @@ -93,6 +83,26 @@ public class NoxesiumOptions { } ); + private static final OptionInstance enableUiLimiting = OptionInstance.createBoolean( + "noxesium.options.ui_limiting.name", + OptionInstance.cachedConstantTooltip(Component.translatable("noxesium.options.ui_limiting.tooltip")), + NoxesiumMod.getInstance().getConfig().enableUiLimiting, + (newValue) -> { + NoxesiumMod.getInstance().getConfig().enableUiLimiting = newValue; + NoxesiumMod.getInstance().getConfig().save(); + } + ); + + private static final OptionInstance enableDynamicUiLimiting = OptionInstance.createBoolean( + "noxesium.options.dynamic_ui_limiting.name", + OptionInstance.cachedConstantTooltip(Component.translatable("noxesium.options.dynamic_ui_limiting.tooltip")), + NoxesiumMod.getInstance().getConfig().enableDynamicUiLimiting, + (newValue) -> { + NoxesiumMod.getInstance().getConfig().enableDynamicUiLimiting = newValue; + NoxesiumMod.getInstance().getConfig().save(); + } + ); + private static final OptionInstance maxUiFramerate = new OptionInstance<>( "noxesium.options.max_ui_framerate.name", OptionInstance.cachedConstantTooltip(Component.translatable("noxesium.options.max_ui_framerate.tooltip")), @@ -111,17 +121,21 @@ public class NoxesiumOptions { ); private static final OptionInstance optimizationOverlay = OptionInstance.createBoolean( - "noxesium.options.optimization_overlay.name", - OptionInstance.cachedConstantTooltip(Component.translatable("noxesium.options.optimization_overlay.tooltip")), - NoxesiumMod.getInstance().getConfig().showOptimizationOverlay, + "noxesium.options.ui_debug_overlay.name", + OptionInstance.cachedConstantTooltip(Component.translatable("noxesium.options.ui_debug_overlay.tooltip")), + NoxesiumMod.getInstance().getConfig().showUiDebugOverlay, (newValue) -> { - NoxesiumMod.getInstance().getConfig().showOptimizationOverlay = newValue; + NoxesiumMod.getInstance().getConfig().showUiDebugOverlay = newValue; NoxesiumMod.getInstance().getConfig().save(); } ); - public static OptionInstance experimentalPatches() { - return experimentalPatches; + public static OptionInstance enableUiLimiting() { + return enableUiLimiting; + } + + public static OptionInstance enableDynamicUiLimiting() { + return enableDynamicUiLimiting; } public static OptionInstance fpsOverlay() { diff --git a/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumSettingsScreen.java b/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumSettingsScreen.java index 82470c75..346c8ef2 100644 --- a/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumSettingsScreen.java +++ b/common/src/main/java/com/noxcrew/noxesium/config/NoxesiumSettingsScreen.java @@ -25,9 +25,10 @@ protected void addOptions() { NoxesiumOptions.playerGlowingKeybinds(), NoxesiumOptions.qibSystemDebugVisuals() ); - this.list.addBig(NoxesiumOptions.experimentalPatches()); + this.list.addBig(NoxesiumOptions.enableUiLimiting()); this.list.addSmall( NoxesiumOptions.maxUiFramerate(), + NoxesiumOptions.enableDynamicUiLimiting(), NoxesiumOptions.optimizationOverlay() ); } diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/entity/SpatialInteractionEntityTree.java b/common/src/main/java/com/noxcrew/noxesium/feature/entity/SpatialInteractionEntityTree.java index 07a18dc7..54b8d3f0 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/entity/SpatialInteractionEntityTree.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/entity/SpatialInteractionEntityTree.java @@ -64,10 +64,8 @@ public static String getSpatialTreeState(Entity entity) { * Rebuilds the model if applicable. */ public static void rebuild() { - if (rebuilding.get()) return; if (!needsRebuilding.get()) return; - - rebuilding.set(true); + if (!rebuilding.compareAndSet(false, true)) return; try { var world = Minecraft.getInstance().level; if (world == null) return; diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/rule/ServerRules.java b/common/src/main/java/com/noxcrew/noxesium/feature/rule/ServerRules.java index afb88e01..34d5493a 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/rule/ServerRules.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/rule/ServerRules.java @@ -55,11 +55,6 @@ public class ServerRules { */ public static ClientServerRule HAND_ITEM_OVERRIDE = register(new ItemStackServerRule(ServerRuleIndices.HAND_ITEM_OVERRIDE)); - /** - * Allows server to override whether experimental UI optimizations are on. - */ - public static ClientServerRule DISABLE_UI_OPTIMIZATIONS = register(new BooleanServerRule(ServerRuleIndices.DISABLE_UI_OPTIMIZATIONS, false)); - /** * Moves the handheld map to be shown in the top left/right corner instead of * in the regular hand slot. diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/LayerWithReference.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/LayerWithReference.java index 258caf51..e70b0cdd 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/LayerWithReference.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/LayerWithReference.java @@ -2,12 +2,14 @@ import com.noxcrew.noxesium.feature.ui.layer.NoxesiumLayer; +import java.util.List; + /** * Holds a layer and a reference to its group. */ public record LayerWithReference( int index, NoxesiumLayer.Layer layer, - NoxesiumLayer.NestedLayers group + List groups ) { } diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/layer/NoxesiumLayer.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/layer/NoxesiumLayer.java index 1778eb3b..7e24d016 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/layer/NoxesiumLayer.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/layer/NoxesiumLayer.java @@ -53,13 +53,6 @@ public List layers() { return inner.layers(); } - /** - * Returns the condition of this group. - */ - public BooleanSupplier condition() { - return condition; - } - /** * Returns whether this group's condition has recently changed. */ diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/layer/NoxesiumLayeredDraw.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/layer/NoxesiumLayeredDraw.java index 554fb012..e47880fc 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/layer/NoxesiumLayeredDraw.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/layer/NoxesiumLayeredDraw.java @@ -1,6 +1,5 @@ package com.noxcrew.noxesium.feature.ui.layer; -import com.noxcrew.noxesium.NoxesiumMod; import com.noxcrew.noxesium.feature.ui.LayerWithReference; import com.noxcrew.noxesium.feature.ui.render.NoxesiumUiRenderState; import com.noxcrew.noxesium.feature.ui.render.SharedVertexBuffer; @@ -21,7 +20,7 @@ * A custom implementation of layered draw that persists groupings * of layers to properly be able to distinguish between them. */ -public class NoxesiumLayeredDraw implements LayeredDraw.Layer, NoxesiumRenderStateHolder { +public class NoxesiumLayeredDraw implements NoxesiumRenderStateHolder { private final List layers = new ArrayList<>(); private final List subgroups = new ArrayList<>(); @@ -59,28 +58,24 @@ public void add(NoxesiumLayer layer) { update(); } - @Override - public void render(GuiGraphics guiGraphics, @NotNull DeltaTracker deltaTracker) { - // If experimental patches are disabled we ignore all custom logic. - if (NoxesiumMod.getInstance().getConfig().shouldDisableExperimentalPerformancePatches()) { + /** + * Resets any lingering data held by this object. + */ + public void reset() { + if (state != null) { // Destroy the state if it exists - if (state != null) { - state.close(); - state = null; - } + state.close(); + state = null; // Ensure everything is disabled! SharedVertexBuffer.reset(); - - // Directly draw everything to the screen - guiGraphics.pose().pushPose(); - for (var layer : layers) { - renderLayerDirectly(guiGraphics, deltaTracker, layer); - } - guiGraphics.pose().popPose(); - return; } + } + /** + * Renders the layered draw's contents. Returns false if it failed to draw properly. + */ + public boolean render(GuiGraphics guiGraphics, @NotNull DeltaTracker deltaTracker) { // Defer rendering the object to the current state, this holds all necessary information // for rendering. if (state == null) { @@ -89,27 +84,7 @@ public void render(GuiGraphics guiGraphics, @NotNull DeltaTracker deltaTracker) // uses them for sub-groups as well. state = new NoxesiumUiRenderState(); } - state.render(guiGraphics, deltaTracker, this); - } - - /** - * Renders a single layer directly, avoiding all custom UI optimizations. - */ - private void renderLayerDirectly(GuiGraphics guiGraphics, DeltaTracker deltaTracker, NoxesiumLayer layer) { - switch (layer) { - case NoxesiumLayer.Layer single -> { - single.layer().render(guiGraphics, deltaTracker); - guiGraphics.pose().translate(0f, 0f, LayeredDraw.Z_SEPARATION); - } - case NoxesiumLayer.NestedLayers group -> { - if (group.condition().getAsBoolean()) { - for (var subLayer : group.layers()) { - renderLayerDirectly(guiGraphics, deltaTracker, subLayer); - } - guiGraphics.pose().translate(0f, 0f, LayeredDraw.Z_SEPARATION); - } - } - } + return state.render(guiGraphics, deltaTracker, this); } /** @@ -121,7 +96,7 @@ public List flatten() { for (var layer : layers) { switch (layer) { case NoxesiumLayer.Layer single -> result.add(new LayerWithReference(result.size() + offset.getValue(), single, null)); - case NoxesiumLayer.NestedLayers group -> process(group, result, offset); + case NoxesiumLayer.NestedLayers group -> process(group, result, offset, new ArrayList<>()); } } return result; @@ -130,11 +105,14 @@ public List flatten() { /** * Adds the contents of the layer group to the given list. */ - private void process(NoxesiumLayer.NestedLayers target, List list, MutableInt offset) { + private void process(NoxesiumLayer.NestedLayers target, List list, MutableInt offset, List nested) { + var nestedCopy = new ArrayList<>(nested); + nestedCopy.add(target); + for (var layer : target.layers()) { switch (layer) { - case NoxesiumLayer.Layer single -> list.add(new LayerWithReference(list.size() + offset.getValue(), single, target)); - case NoxesiumLayer.NestedLayers group -> process(group, list, offset); + case NoxesiumLayer.Layer single -> list.add(new LayerWithReference(list.size() + offset.getValue(), single, nestedCopy)); + case NoxesiumLayer.NestedLayers group -> process(group, list, offset, nestedCopy); } } 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/ElementBuffer.java index ee31a61d..eccbcde5 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/ElementBuffer.java @@ -51,7 +51,7 @@ public void updateBlendState(@Nullable BlendState blendState) { } /** - * Returns the associated blend state of the this buffer. + * Returns the associated blend state of this buffer. */ @Nullable public BlendState getBlendState() { @@ -95,6 +95,7 @@ public void snapshot() { // texture into it directly pbos[currentIndex].bind(); + // TODO This causes micro-stutters! var window = Minecraft.getInstance().getWindow(); GL11.glReadPixels(0, 0, 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/ElementBufferGroup.java index e7f242f6..09daae37 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/ElementBufferGroup.java @@ -35,17 +35,6 @@ public List layers() { return Collections.unmodifiableList(layers); } - /** - * Draws this group directly to the screen. - */ - public void drawDirectly(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { - for (var layer : layers) { - if (layer.group() == null || layer.group().test()) { - renderLayer(guiGraphics, deltaTracker, layer.layer(), layer.index()); - } - } - } - /** * Adds the given layers to this group. */ 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 5715fe65..6f5d1813 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 @@ -1,5 +1,6 @@ package com.noxcrew.noxesium.feature.ui.render; +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; @@ -19,7 +20,6 @@ public class NoxesiumUiRenderState implements NoxesiumRenderState { private final List groups = new CopyOnWriteArrayList<>(); private final Random random = new Random(); - private final double updateFps = 0.5; private long nextUpdate = -1; private int lastSize = 0; @@ -33,64 +33,12 @@ public List groups() { /** * Renders the given layered draw object to the screen. */ - public void render(GuiGraphics guiGraphics, DeltaTracker deltaTracker, NoxesiumLayeredDraw layeredDraw) { + public boolean render(GuiGraphics guiGraphics, DeltaTracker deltaTracker, NoxesiumLayeredDraw layeredDraw) { var nanoTime = System.nanoTime(); + var dynamic = NoxesiumMod.getInstance().getConfig().enableDynamicUiLimiting; - // Update which groups exist - if (lastSize != layeredDraw.size()) { - lastSize = layeredDraw.size(); - resetGroups(); - - // Determine all layers ordered and flattened, then - // split them up into - var flattened = layeredDraw.flatten(); - - // Start by splitting into 4 partitions - var chunked = chunked(flattened, flattened.size() / 4); - for (var chunk : chunked) { - var group = new ElementBufferGroup(); - group.addLayers(chunk); - groups.add(group); - } - } - - // Update for each group what the condition is - for (var group : layeredDraw.subgroups()) { - group.update(); - } - - // Try to split up or merge together groups, but don't run this too frequently! - if (nextUpdate == -1) { - nextUpdate = nanoTime; - } - if (nanoTime >= nextUpdate) { - // Schedule when we can next update the groups - nextUpdate = nanoTime + (long) Math.floor(((1 / updateFps) * random.nextDouble() * 1000000000)); - - // Iterate through all groups and make changes - var index = 0; - while (index < groups.size()) { - var group = groups.get(index++); - - // Try to merge if there are neighboring groups - if (index > 2 && index < groups.size()) { - if (group.canMerge(groups.get(index))) { - group.join(groups.get(index)); - groups.remove(index).close(); - index--; - } else if (groups.get(index - 2).canMerge(group)) { - groups.get(index - 2).join(group); - groups.remove(index - 1).close(); - index--; - } - } - - // Try to split up the group - if (group.shouldSplit()) { - groups.add(index++, group.split()); - } - } - } + // Update all groups, re-ordering where each layer is located + updateGroups(layeredDraw, nanoTime, dynamic); // Tick the groups, possibly redrawing the buffer contents, if any buffers got drawn to // we want to unbind the buffer afterwards @@ -98,18 +46,27 @@ public void render(GuiGraphics guiGraphics, DeltaTracker deltaTracker, NoxesiumL // Determine if the group has recently changed their // visibility state, if so request an immediate redraw! for (var layer : group.layers()) { - if (layer.group() != null && layer.group().hasChangedRecently()) { - group.dynamic().redraw(); - break; + if (layer.groups() == null) continue; + for (var layerGroup : layer.groups()) { + if (layerGroup.hasChangedRecently()) { + group.dynamic().redraw(); + break; + } } } // Update the dynamic element of the group group.dynamic().update(nanoTime, guiGraphics, () -> { + outer: for (var layer : group.layers()) { - if (layer.group() == null || layer.group().test()) { - group.renderLayer(guiGraphics, deltaTracker, layer.layer(), layer.index()); + if (layer.groups() != null) { + for (var layerGroup : layer.groups()) { + if (!layerGroup.test()) { + continue outer; + } + } } + group.renderLayer(guiGraphics, deltaTracker, layer.layer(), layer.index()); } }); } @@ -117,24 +74,85 @@ public void render(GuiGraphics guiGraphics, DeltaTracker deltaTracker, NoxesiumL // Unbind the frame buffers BufferHelper.unbind(); - // Draw the groups in order + // If any group is invalid we give up + for (var group : groups) { + if (group.dynamic().isInvalid()) return false; + } + + // Draw all groups to the screen together var ids = new ArrayList(); for (var group : groups) { - // If the buffer is broken we have to early exit and draw - // directly before going back to the buffers! - if (group.dynamic().isInvalid()) { - SharedVertexBuffer.draw(ids); - ids.clear(); - group.drawDirectly(guiGraphics, deltaTracker); - continue; + group.dynamic().submitTextureIds(ids); + } + SharedVertexBuffer.draw(ids); + return true; + } + + /** + * Updates which groups are currently registered in this render state. + * Also ticks their visibility state. + */ + private void updateGroups(NoxesiumLayeredDraw layeredDraw, long nanoTime, boolean dynamic) { + // Update which groups exist if the layered draw object has changed + var intendedSize = dynamic ? layeredDraw.size() : 1; + if (lastSize != intendedSize) { + lastSize = intendedSize; + resetGroups(); + + // Determine all layers ordered and flattened, then + // split them up into partitions if we are in dynamic mode + var flattened = layeredDraw.flatten(); + if (dynamic) { + var chunked = chunked(flattened, flattened.size() / 4); + for (var chunk : chunked) { + var group = new ElementBufferGroup(); + group.addLayers(chunk); + groups.add(group); + } + } else { + var group = new ElementBufferGroup(); + group.addLayers(flattened); + groups.add(group); } + } - // If the buffer is valid we use it to draw - group.dynamic().submitTextureIds(ids); + // Update for each group what the condition is + for (var group : layeredDraw.subgroups()) { + group.update(); } - // Call draw on any remaining ids - SharedVertexBuffer.draw(ids); + // If we're in dynamic mode we try to update groups. + if (dynamic) { + // Try to split up or merge together groups, but don't run this too frequently! + if (nextUpdate == -1 || nanoTime >= nextUpdate) { + // Schedule when we can next update the groups + nextUpdate = nanoTime + random.nextLong(500000000); + + // Iterate through all groups and make changes + var index = 0; + while (index < groups.size()) { + var group = groups.get(index++); + + // Try to merge if there are neighboring groups + if (index > 2 && index < groups.size()) { + if (group.canMerge(groups.get(index))) { + group.join(groups.get(index)); + groups.remove(index).close(); + index--; + } else if (groups.get(index - 2).canMerge(group)) { + groups.get(index - 2).join(group); + groups.remove(index - 1).close(); + index--; + } + } + + // Try to split up the group + if (group.shouldSplit()) { + groups.add(index++, group.split()); + } + } + } + } } /** 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 9f853449..e71f2c74 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 @@ -45,15 +45,9 @@ public class SharedVertexBuffer implements Closeable { * Resets any cached values. */ public static void reset() { - if (blendStateHook != null) { - blendStateHook = null; - } - if (!allowRebindingTarget) { - allowRebindingTarget = true; - } - if (ignoreBlendStateHook) { - ignoreBlendStateHook = false; - } + blendStateHook = null; + allowRebindingTarget = true; + ignoreBlendStateHook = false; } /** diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/NoxesiumRenderStateHolder.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/NoxesiumRenderStateHolder.java index c629c432..a45a64be 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/NoxesiumRenderStateHolder.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/api/NoxesiumRenderStateHolder.java @@ -1,5 +1,6 @@ package com.noxcrew.noxesium.feature.ui.render.api; +import com.noxcrew.noxesium.NoxesiumMod; import org.jetbrains.annotations.Nullable; /** @@ -32,6 +33,9 @@ default void updateRenderFramerate() { * Indicates that a check should run the very next frame. */ default void requestCheck() { + // Ignore checks if we're not using dynamic UI limiting! + if (!NoxesiumMod.getInstance().getConfig().enableDynamicUiLimiting) return; + var state = get(); if (state != null) { state.requestCheck(); 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 4be0a049..8525f136 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 @@ -28,7 +28,7 @@ public DynamicElement dynamic() { /** * Renders the given screen. */ - public void render(GuiGraphics guiGraphics, int width, int height, float deltaTime, Screen screen) { + public boolean render(GuiGraphics guiGraphics, int width, int height, float deltaTime, Screen screen) { var nanoTime = System.nanoTime(); // Try to update the buffer @@ -42,15 +42,13 @@ public void render(GuiGraphics guiGraphics, int width, int height, float deltaTi BufferHelper.unbind(); // If the buffer is invalid we draw directly instead of using it - if (dynamic.isInvalid()) { - screen.renderWithTooltip(guiGraphics, width, height, deltaTime); - return; - } + if (dynamic.isInvalid()) return false; // If the buffer is valid we use it to draw var ids = new ArrayList(); dynamic.submitTextureIds(ids); SharedVertexBuffer.draw(ids); + return true; } @Override diff --git a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/screen/ScreenRenderingHolder.java b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/screen/ScreenRenderingHolder.java index 33ea39b9..c4fe2599 100644 --- a/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/screen/ScreenRenderingHolder.java +++ b/common/src/main/java/com/noxcrew/noxesium/feature/ui/render/screen/ScreenRenderingHolder.java @@ -24,11 +24,11 @@ public static ScreenRenderingHolder getInstance() { /** * Renders the on-screen menu. */ - public void render(GuiGraphics guiGraphics, int width, int height, float deltaTime, Screen screen) { + public boolean render(GuiGraphics guiGraphics, int width, int height, float deltaTime, Screen screen) { if (state == null) { state = new NoxesiumScreenRenderState(); } - state.render(guiGraphics, width, height, deltaTime, screen); + return state.render(guiGraphics, width, height, deltaTime, screen); } @Override diff --git a/common/src/main/java/com/noxcrew/noxesium/mixin/ui/CustomDebugHotkeysMixin.java b/common/src/main/java/com/noxcrew/noxesium/mixin/ui/CustomDebugHotkeysMixin.java index 54587c49..9c092c23 100644 --- a/common/src/main/java/com/noxcrew/noxesium/mixin/ui/CustomDebugHotkeysMixin.java +++ b/common/src/main/java/com/noxcrew/noxesium/mixin/ui/CustomDebugHotkeysMixin.java @@ -4,8 +4,6 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mojang.blaze3d.platform.InputConstants; -import com.noxcrew.noxesium.NoxesiumMod; -import com.noxcrew.noxesium.config.NoxesiumConfig; import com.noxcrew.noxesium.config.NoxesiumSettingsScreen; import net.minecraft.Util; import net.minecraft.client.KeyboardHandler; @@ -17,8 +15,6 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import java.util.Objects; - /** * Adds various debug hotkeys for Noxesium. */ @@ -28,38 +24,21 @@ public abstract class CustomDebugHotkeysMixin { @Shadow private long debugCrashKeyTime; - @Shadow - protected abstract void debugFeedbackTranslated(String string, Object... objects); - @WrapOperation(method = "handleDebugKeys", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/ChatComponent;addMessage(Lnet/minecraft/network/chat/Component;)V")) - public void addExperimentalPatchesHelpMessage(ChatComponent instance, Component component, Operation original) { + public void extendHelpMessage(ChatComponent instance, Component component, Operation original) { if (component.getContents() instanceof TranslatableContents translatableContents) { if (translatableContents.getKey().equals("debug.pause.help")) { instance.addMessage(Component.translatable("debug.noxesium_settings.help")); - if (NoxesiumMod.getInstance().getConfig().hasConfiguredPerformancePatches()) { - instance.addMessage(Component.translatable("debug.experimental_patches.help")); - } } } original.call(instance, component); } @ModifyReturnValue(method = "handleDebugKeys", at = @At("TAIL")) - public boolean toggleExperimentalPatches(boolean original, int keyCode) { + public boolean openSettingsMenu(boolean original, int keyCode) { if (this.debugCrashKeyTime > 0L && this.debugCrashKeyTime < Util.getMillis() - 100L) { return original; } - - if (keyCode == InputConstants.KEY_W && NoxesiumMod.getInstance().getConfig().hasConfiguredPerformancePatches()) { - if (Objects.equals(NoxesiumConfig.experimentalPatchesHotkey, false)) { - NoxesiumConfig.experimentalPatchesHotkey = true; - this.debugFeedbackTranslated("debug.experimental_patches.enabled"); - } else { - NoxesiumConfig.experimentalPatchesHotkey = false; - this.debugFeedbackTranslated("debug.experimental_patches.disabled"); - } - return true; - } if (keyCode == InputConstants.KEY_V) { Minecraft.getInstance().setScreen(new NoxesiumSettingsScreen(null)); return true; 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 400ff526..5c7eb61d 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 @@ -2,7 +2,6 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.noxcrew.noxesium.NoxesiumMod; -import com.noxcrew.noxesium.config.NoxesiumConfig; import com.noxcrew.noxesium.feature.entity.SpatialInteractionEntityTree; import com.noxcrew.noxesium.feature.rule.ServerRules; import com.noxcrew.noxesium.feature.ui.CustomMapUiWidget; @@ -71,20 +70,16 @@ public abstract class GuiMixin { text.add(Component.translatable("debug.game_time_overlay", String.format("%.5f", RenderSystem.getShaderGameTime()), (int) (RenderSystem.getShaderGameTime() * 24000))); } - // Add qib system debug information + // Add debug overlays if enabled, these are not using translations as they are purely for debugging purposes! + // Start with qib system debug information if (NoxesiumMod.getInstance().getConfig().enableQibSystemDebugging && minecraft.player != null) { text.add(Component.literal("§bEntities in model: §7" + SpatialInteractionEntityTree.getModelContents().size())); text.add(Component.literal("§bIn water: " + (minecraft.player.isInWaterOrRain() ? "§aYes" : minecraft.player.noxesium$hasTridentCoyoteTime() ? "§eGrace" : "§cNo"))); text.add(Component.literal("§bQib behavior amount: §7" + ServerRules.QIB_BEHAVIORS.getValue().size())); } - // Show extra text if the experimental patches are on - if (NoxesiumConfig.experimentalPatchesHotkey != null) { - text.add(Component.translatable("debug.experimental_patches." + (NoxesiumConfig.experimentalPatchesHotkey ? "on" : "off"))); - } - - // If the experimental patches are on we draw the current UI frame rates and group layouts - if (NoxesiumMod.getInstance().getConfig().showOptimizationOverlay) { + // If the dynamic UI updating is on we draw the current UI frame rates and group layouts + if (NoxesiumMod.getInstance().getConfig().enableDynamicUiLimiting && NoxesiumMod.getInstance().getConfig().showUiDebugOverlay) { NoxesiumMod.forEachRenderStateHolder((it) -> { var stateIn = it.get(); switch (stateIn) { @@ -141,8 +136,7 @@ public void onInit(Minecraft minecraft, CallbackInfo ci) { noxesium$addRenderLayer("Noxesium Text Overlay", this::noxesium$renderTextOverlay, () -> !this.getDebugOverlay().showDebugScreen() && (NoxesiumMod.getInstance().getConfig().showFpsOverlay || NoxesiumMod.getInstance().getConfig().showGameTimeOverlay || - NoxesiumMod.getInstance().getConfig().enableQibSystemDebugging || - NoxesiumConfig.experimentalPatchesHotkey != null) + NoxesiumMod.getInstance().getConfig().enableQibSystemDebugging) ); } } diff --git a/common/src/main/java/com/noxcrew/noxesium/mixin/ui/LayeredDrawMixin.java b/common/src/main/java/com/noxcrew/noxesium/mixin/ui/LayeredDrawMixin.java index 8a11506b..a79edeac 100644 --- a/common/src/main/java/com/noxcrew/noxesium/mixin/ui/LayeredDrawMixin.java +++ b/common/src/main/java/com/noxcrew/noxesium/mixin/ui/LayeredDrawMixin.java @@ -2,9 +2,11 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.noxcrew.noxesium.NoxesiumMod; import com.noxcrew.noxesium.feature.ui.layer.LayeredDrawExtension; import com.noxcrew.noxesium.feature.ui.layer.NoxesiumLayer; import com.noxcrew.noxesium.feature.ui.layer.NoxesiumLayeredDraw; +import com.noxcrew.noxesium.feature.ui.render.SharedVertexBuffer; import net.minecraft.client.DeltaTracker; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.LayeredDraw; @@ -22,6 +24,9 @@ public class LayeredDrawMixin implements LayeredDrawExtension { @Unique private final NoxesiumLayeredDraw noxesium$layeredDraw = new NoxesiumLayeredDraw(); + @Unique + private boolean noxesium$ignoreAdditions = false; + @Override public NoxesiumLayeredDraw noxesium$get() { return noxesium$layeredDraw; @@ -30,22 +35,34 @@ public class LayeredDrawMixin implements LayeredDrawExtension { @Override public void noxesium$addLayer(String name, LayeredDraw.Layer layer) { noxesium$layeredDraw.add(new NoxesiumLayer.Layer(name, layer)); + noxesium$ignoreAdditions = true; + ((LayeredDraw) (Object) this).add(layer); + noxesium$ignoreAdditions = false; } @WrapMethod(method = "add(Lnet/minecraft/client/gui/LayeredDraw$Layer;)Lnet/minecraft/client/gui/LayeredDraw;") private LayeredDraw addLayer(LayeredDraw.Layer layer, Operation original) { - noxesium$layeredDraw.add(new NoxesiumLayer.Layer(layer)); - return ((LayeredDraw) (Object) this); + if (!noxesium$ignoreAdditions) noxesium$layeredDraw.add(new NoxesiumLayer.Layer(layer)); + return original.call(layer); } @WrapMethod(method = "add(Lnet/minecraft/client/gui/LayeredDraw;Ljava/util/function/BooleanSupplier;)Lnet/minecraft/client/gui/LayeredDraw;") private LayeredDraw addGroup(LayeredDraw layeredDraw, BooleanSupplier booleanSupplier, Operation original) { noxesium$layeredDraw.add(new NoxesiumLayer.NestedLayers(((LayeredDrawExtension) layeredDraw).noxesium$get(), booleanSupplier)); - return ((LayeredDraw) (Object) this); + return original.call(layeredDraw, booleanSupplier); } @WrapMethod(method = "render") private void render(GuiGraphics guiGraphics, DeltaTracker deltaTracker, Operation original) { - noxesium$layeredDraw.render(guiGraphics, deltaTracker); + // Try to render through the UI limiting system, otherwise fall back to vanilla! + if (NoxesiumMod.getInstance().getConfig().enableUiLimiting) { + if (noxesium$layeredDraw.render(guiGraphics, deltaTracker)) return; + + // Reset the state if the rendering fails! + SharedVertexBuffer.reset(); + } else { + noxesium$layeredDraw.reset(); + } + original.call(guiGraphics, deltaTracker); } } diff --git a/common/src/main/java/com/noxcrew/noxesium/mixin/ui/ScreenRenderHookMixin.java b/common/src/main/java/com/noxcrew/noxesium/mixin/ui/ScreenRenderHookMixin.java index 8097d68a..07ede63f 100644 --- a/common/src/main/java/com/noxcrew/noxesium/mixin/ui/ScreenRenderHookMixin.java +++ b/common/src/main/java/com/noxcrew/noxesium/mixin/ui/ScreenRenderHookMixin.java @@ -19,18 +19,15 @@ public class ScreenRenderHookMixin { @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/Screen;renderWithTooltip(Lnet/minecraft/client/gui/GuiGraphics;IIF)V")) public void renderScreen(Screen instance, GuiGraphics guiGraphics, int width, int height, float deltaTime) { - // If experimental patches are disabled we ignore all custom logic, - // or if we are not in a GUI menu. - if (!(instance instanceof MenuAccess || instance instanceof ChatScreen) || NoxesiumMod.getInstance().getConfig().shouldDisableExperimentalPerformancePatches()) { + if ((instance instanceof MenuAccess || instance instanceof ChatScreen) && NoxesiumMod.getInstance().getConfig().enableUiLimiting) { + // Create a new state object and let it render + if (ScreenRenderingHolder.getInstance().render(guiGraphics, width, height, deltaTime, instance)) return; + } else { // Destroy the state if it exists ScreenRenderingHolder.getInstance().clear(); - - // Directly draw everything to the screen - instance.renderWithTooltip(guiGraphics, width, height, deltaTime); - return; } - // Create a new state object and let it render - ScreenRenderingHolder.getInstance().render(guiGraphics, width, height, deltaTime, instance); + // Directly draw everything to the screen + instance.renderWithTooltip(guiGraphics, width, height, deltaTime); } } diff --git a/common/src/main/resources/assets/noxesium/lang/en_us.json b/common/src/main/resources/assets/noxesium/lang/en_us.json index 82f167de..b1f82983 100644 --- a/common/src/main/resources/assets/noxesium/lang/en_us.json +++ b/common/src/main/resources/assets/noxesium/lang/en_us.json @@ -1,21 +1,11 @@ { - "debug.experimental_patches.disabled": "Noxesium UI performance patches are now off", - "debug.experimental_patches.enabled": "Noxesium UI performance patches are now on", - "debug.experimental_patches.help": "F3 + W = Toggle Noxesium UI performance patches", "debug.noxesium_settings.help": "F3 + V = Open Noxesium settings menu", - "debug.fps_overlay.disabled": "FPS counter is now off", - "debug.fps_overlay.enabled": "FPS counter is now on (turn off with F3 + Y)", - "debug.fps_overlay.help": "F3 + Y = Toggle on-screen fps counter", "debug.fps_overlay": "%s fps", + "debug.game_time_overlay": "%s st, %s gt", "noxesium.options.game_time_overlay.name": "Game Time Overlay", "noxesium.options.game_time_overlay.tooltip": "Adds a small overlay that shows the current game time.\n\n§cIntended for shader development.", - "debug.game_time_overlay": "%s st, %s gt", - - "debug.experimental_patches.on": "Experimental Optimizations: §aOn", - "debug.experimental_patches.off": "Experimental Optimizations: §cOff", - "noxesium.options.pages.noxesium": "Noxesium", "noxesium.options.screen.noxesium": "Noxesium Settings", "noxesium.options.fps_overlay.name": "FPS Overlay", @@ -40,14 +30,14 @@ "noxesium.options.enable_glowing_keybinds.name": "Team Glow Keybinds", "noxesium.options.enable_glowing_keybinds.tooltip": "Adds additional keybinds which make players glow based on their team color. Requires restarting game to apply!\n\n§cKeybinds only work while spectating.", - "noxesium.options.experimental_patches.name": "Experimental Optimizations", - "noxesium.options.experimental_patches.tooltip.v2": "§fEnables experimental optimizations that dynamically adjust the framerate at which UI elements are rendered.\n\n§cWarning: If you experience any UI rendering issues while using this setting, please report issues to Noxesium, not the other mods involved.", - "noxesium.options.min_ui_framerate.name": "Min UI Framerate", - "noxesium.options.min_ui_framerate.tooltip": "Sets the lowest framerate at which UI elements are rendered. This controls how quickly the UI will adapt to controls.\n\n§eRecommended Value: §f30 fps", + "noxesium.options.ui_limiting.name": "Limit UI Framerate", + "noxesium.options.ui_limiting.tooltip": "§fRenders the UI at the set Max UI Framerate instead of every frame. This can improve fps at high values.", "noxesium.options.max_ui_framerate.name": "Max UI Framerate", - "noxesium.options.max_ui_framerate.tooltip": "Sets the highest framerate at which UI elements are rendered.\n\n§cSetting this to Unlimited has a very high performance impact!\n\n§eRecommended Value: §f60 fps", - "noxesium.options.optimization_overlay.name": "UI Optimization Overlay", - "noxesium.options.optimization_overlay.tooltip": "Adds a small overlay that shows debug information about the UI rendering optimizations.", + "noxesium.options.max_ui_framerate.tooltip": "Sets the highest framerate at which UI elements are rendered. Only used if `Limit UI Framerate` is ON.", + "noxesium.options.dynamic_ui_limiting.name": "Dynamic UI Optimization", + "noxesium.options.dynamic_ui_limiting.tooltip": "§fEnables additional optimizations that dynamically adjust the framerate at which UI elements are rendered, capped at the Max UI Framerate.\n\n§cMay not result in performance improvements for all users, use with caution!", + "noxesium.options.ui_debug_overlay.name": "Debug Dynamic UI", + "noxesium.options.ui_debug_overlay.tooltip": "Adds a small overlay that shows debug information about the dynamic UI rendering optimizations.", "noxesium.options.enum.default": "Ask server", "noxesium.options.enum.true": "Always",