From 4d6db78059cf4db3494f3e6762905136c337d727 Mon Sep 17 00:00:00 2001 From: austin Date: Sun, 10 Nov 2024 14:51:29 -0500 Subject: [PATCH] ClientDrawCellManager optimizations --- .gitignore | 7 +- .vscode/launch.json | 9 +- docs/src/progress/renderertodo.md | 4 + .../client/scene/ClientSceneWrapper.java | 2 +- .../client/terrain/cache/ChunkData.java | 24 +++- .../terrain/cache/ClientTerrainCache.java | 60 ++++++++-- .../terrain/cells/ClientDrawCellManager.java | 7 +- .../client/terrain/cells/DrawCell.java | 4 +- .../terrain/manager/ClientTerrainManager.java | 30 ++--- .../ui/menu/debug/ImGuiChunkMonitor.java | 5 +- .../client/ui/menu/debug/ImGuiMemory.java | 105 ++++++++++++++++++ .../ui/menu/debug/ImGuiWindowMacros.java | 8 +- .../collision/CollisionEngine.java | 4 +- .../java/electrosphere/engine/Globals.java | 4 + src/main/java/electrosphere/engine/Main.java | 2 +- .../entity/types/terrain/TerrainChunk.java | 2 +- .../net/client/protocol/EntityProtocol.java | 2 +- .../net/client/protocol/TerrainProtocol.java | 2 +- .../TestGenerationChunkGenerator.java | 61 +++++----- .../terrain/manager/ServerChunkCache.java | 10 +- .../cells/ClientDrawCellManagerTests.java | 1 + 21 files changed, 272 insertions(+), 81 deletions(-) create mode 100644 src/main/java/electrosphere/client/ui/menu/debug/ImGuiMemory.java diff --git a/.gitignore b/.gitignore index b1e0ea27..5d2b19d9 100644 --- a/.gitignore +++ b/.gitignore @@ -62,4 +62,9 @@ */~$*.xlsx #General cache for the engine -/.cache \ No newline at end of file +/.cache + +#Memory debug files +/heap.dump +/heap.dump.hwcache +/tmp \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index f289bddf..dc095bc6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,14 +8,15 @@ "type": "java", "name": "Launch Current File", "request": "launch", - "mainClass": "${file}" + "mainClass": "${file}", + "vmArgs": "-Xmx4G -Xms1024m -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.dump\"" }, { "type": "java", "name": "Launch Main", "request": "launch", "mainClass": "electrosphere.engine.Main", - "vmArgs": "-Xmx2G -Xms100m -XX:+UseZGC -XX:+UseDynamicNumberOfGCThreads -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"/tmp\"", + "vmArgs": "-Xmx4G -Xms1024m -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.dump\"", "projectName": "Renderer" }, { @@ -23,7 +24,7 @@ "name": "Launch Main (Debug Memory)", "request": "launch", "mainClass": "electrosphere.engine.Main", - "vmArgs": "-Xmx2G -Xms100m -XX:+UseZGC -XX:+UseDynamicNumberOfGCThreads -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"/tmp\" -javaagent:./lwjglx-debug-1.0.0.jar=t;o=trace.log", + "vmArgs": "-Xmx4G -Xms1024m -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.dump\" -javaagent:./lwjglx-debug-1.0.0.jar=t;o=trace.log", "projectName": "Renderer" }, { @@ -34,7 +35,7 @@ "env": { "ALSOFT_LOGLEVEL": 4, }, - "vmArgs": "-Xmx2G -Xms100m -XX:+UseZGC -XX:+UseDynamicNumberOfGCThreads -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"/tmp\"", + "vmArgs": "-Xmx4G -Xms1024m -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.dump\"", "projectName": "Renderer" }, { diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index a5f4b786..324f9e19 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -973,6 +973,10 @@ ScriptEngine full re-initialization signal Add surface width to test generator User setting to toggle foliage manager Fix client terrain cache lookup bug +Memory debugging work + update memory flags in launch file + +(11/10/2024) +Attempts at optimizing ClientDrawCellManager # TODO diff --git a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java index ff4e5523..8602a3b9 100644 --- a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java +++ b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java @@ -122,7 +122,7 @@ public class ClientSceneWrapper { * @param clientEntity The client entity */ public void deregisterTranslationMapping(Entity clientEntity){ - if(clientToServerMapContainsId(clientEntity.getId())){ + if(this.clientToServerMapContainsId(clientEntity.getId())){ //remove from client->server map int serverId = clientToServerIdMap.remove(clientEntity.getId()); //remove from server->client map diff --git a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java index 3c9f5762..9275d45c 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java +++ b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java @@ -17,6 +17,11 @@ public class ChunkData { */ public static final int NO_STRIDE = 0; + /** + * The id for a non-homogenous data + */ + public static final int NOT_HOMOGENOUS = -1; + //The size of a chunk in virtual data public static final int CHUNK_SIZE = ServerTerrainChunk.CHUNK_DIMENSION; //The size of the data passed into marching cubes/transvoxel algorithm to get a fully connected and seamless chunk @@ -35,10 +40,12 @@ public class ChunkData { * The word x coordinate */ int worldX; + /** * The word y coordinate */ int worldY; + /** * The word z coordinate */ @@ -49,18 +56,25 @@ public class ChunkData { */ int stride; + /** + * Tracks whether this chunk is homogenous or not + */ + int homogenousValue = NOT_HOMOGENOUS; + /** * Creates a chunk data * @param worldX The word x coordinate * @param worldY The word y coordinate * @param worldZ The word z coordinate * @param stride The stride of the data + * @param homogenous Tracks whether this chunk is homogenous or not */ - public ChunkData(int worldX, int worldY, int worldZ, int stride){ + public ChunkData(int worldX, int worldY, int worldZ, int stride, int homogenousValue){ this.worldX = worldX; this.worldY = worldY; this.worldZ = worldZ; this.stride = stride; + this.homogenousValue = homogenousValue; } @@ -231,5 +245,13 @@ public class ChunkData { return stride; } + /** + * The homogenous value of the chunk data + * @return if the data is homogenous, will return the id of the voxel that comprises the whole data block. Otherwise will return ChunkData.NOT_HOMOGENOUS + */ + public int getHomogenousValue(){ + return homogenousValue; + } + } diff --git a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java index fe8c97fe..2b85e2b3 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java +++ b/src/main/java/electrosphere/client/terrain/cache/ClientTerrainCache.java @@ -1,10 +1,10 @@ package electrosphere.client.terrain.cache; import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Semaphore; import org.joml.Vector3i; @@ -25,37 +25,37 @@ public class ClientTerrainCache { /** * The map of full res chunk key -> chunk data */ - Map cacheMapFullRes = new ConcurrentHashMap(); + Map cacheMapFullRes = new HashMap(); /** * The map of half res chunk key -> chunk data */ - Map cacheMapHalfRes = new ConcurrentHashMap(); + Map cacheMapHalfRes = new HashMap(); /** * The map of quarter res chunk key -> chunk data */ - Map cacheMapQuarterRes = new ConcurrentHashMap(); + Map cacheMapQuarterRes = new HashMap(); /** * The map of eighth res chunk key -> chunk data */ - Map cacheMapEighthRes = new ConcurrentHashMap(); + Map cacheMapEighthRes = new HashMap(); /** * The map of sixteenth res chunk key -> chunk data */ - Map cacheMapSixteenthRes = new ConcurrentHashMap(); + Map cacheMapSixteenthRes = new HashMap(); /** * The list of keys in the cache */ - List cacheList = new CopyOnWriteArrayList(); + List cacheList = new LinkedList(); /** * A map of chunk to its world position */ - Map chunkPositionMap = new ConcurrentHashMap(); + Map chunkPositionMap = new HashMap(); /** * The lock on the terrain cache @@ -82,9 +82,47 @@ public class ClientTerrainCache { Map cache = this.getCache(chunkData.getStride()); cache.put(getKey(worldX,worldY,worldZ),chunkData); chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ)); + cacheList.add(this.getKey(worldX,worldY,worldZ)); while(cacheList.size() > cacheSize){ - Long currentChunk = cacheList.remove(0); - cache.remove(currentChunk); + Long currentKey = cacheList.remove(0); + ChunkData chunk = null; + ChunkData removed = null; + removed = cacheMapFullRes.remove(currentKey); + if(removed != null){ + chunk = removed; + } + removed = cacheMapHalfRes.remove(currentKey); + if(removed != null){ + chunk = removed; + } + removed = cacheMapQuarterRes.remove(currentKey); + if(removed != null){ + chunk = removed; + } + removed = cacheMapEighthRes.remove(currentKey); + if(removed != null){ + chunk = removed; + } + removed = cacheMapSixteenthRes.remove(currentKey); + if(removed != null){ + chunk = removed; + } + chunkPositionMap.remove(chunk); + } + if(cacheMapFullRes.size() > cacheSize){ + throw new Error("Client cache surpassed designated size! " + cacheMapFullRes.size()); + } + if(cacheMapHalfRes.size() > cacheSize){ + throw new Error("Client cache surpassed designated size! " + cacheMapHalfRes.size()); + } + if(cacheMapQuarterRes.size() > cacheSize){ + throw new Error("Client cache surpassed designated size! " + cacheMapQuarterRes.size()); + } + if(cacheMapEighthRes.size() > cacheSize){ + throw new Error("Client cache surpassed designated size! " + cacheMapEighthRes.size()); + } + if(cacheMapSixteenthRes.size() > cacheSize){ + throw new Error("Client cache surpassed designated size! " + cacheMapSixteenthRes.size()); } lock.release(); } diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index 1ec394b2..3185ee51 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -36,7 +36,7 @@ public class ClientDrawCellManager { /** * The distance to draw at full resolution */ - public static final double FULL_RES_DIST = 12 * ServerTerrainChunk.CHUNK_DIMENSION; + public static final double FULL_RES_DIST = 8 * ServerTerrainChunk.CHUNK_DIMENSION; /** * The distance for half resolution @@ -301,8 +301,9 @@ public class ClientDrawCellManager { updated = true; } else if(!node.isLeaf()){ this.validCellCount++; - List> children = new LinkedList>(node.getChildren()); - for(FloatingChunkTreeNode child : children){ + List> children = node.getChildren(); + for(int i = 0; i < 8; i++){ + FloatingChunkTreeNode child = children.get(i); boolean childUpdate = recursivelyUpdateCells(child, playerPos, evaluationMap, minLeafLod); if(childUpdate == true){ updated = true; diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 9c8d2463..4927e68f 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -92,7 +92,7 @@ public class DrawCell { * Generates a drawable entity based on this chunk */ public void generateDrawableEntity(VoxelTextureAtlas atlas, int lod, List higherLODFaces){ - Globals.profiler.beginCpuSample("DrawCell.fillInData"); + Globals.profiler.beginAggregateCpuSample("DrawCell.fillInData"); boolean success = this.fillInData(lod); Globals.profiler.endCpuSample(); if(!success){ @@ -143,8 +143,8 @@ public class DrawCell { * Destroys a drawcell including its physics */ public void destroy(){ - CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); if(modelEntity != null){ + CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); collisionEngine.destroyPhysics(modelEntity); ClientEntityUtils.destroyEntity(modelEntity); } diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index 2efa7702..e744bff4 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -4,12 +4,11 @@ import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Semaphore; import org.joml.Vector3i; @@ -35,7 +34,7 @@ import io.github.studiorailgun.HashUtils; public class ClientTerrainManager { //queues messages from server - List messageQueue = new CopyOnWriteArrayList(); + List messageQueue = new LinkedList(); /** * Locks the terrain manager (eg if adding message from network) @@ -56,7 +55,7 @@ public class ClientTerrainManager { public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO; //caches chunks from server - static final int CACHE_SIZE = 1500 + (int)(ClientDrawCellManager.FULL_RES_DIST * 10) + (int)(ClientDrawCellManager.HALF_RES_DIST * 10); + static final int CACHE_SIZE = 2500 + (int)(ClientDrawCellManager.FULL_RES_DIST) + (int)(ClientDrawCellManager.HALF_RES_DIST); /** * Size of the cache in bytes @@ -70,12 +69,12 @@ public class ClientTerrainManager { ClientWorldData clientWorldData; //The queue of terrain chunk data to be buffered to gpu - static List terrainChunkGenerationQueue = new CopyOnWriteArrayList(); + static List terrainChunkGenerationQueue = new LinkedList(); /** * Tracks what outgoing requests are currently active */ - Map requestedMap = new ConcurrentHashMap(); + Map requestedMap = new HashMap(); /** * Constructor @@ -93,7 +92,6 @@ public class ClientTerrainManager { lock.acquireUninterruptibly(); List bouncedMessages = new LinkedList(); for(TerrainMessage message : messageQueue){ - messageQueue.remove(message); switch(message.getMessageSubtype()){ case SENDCHUNKDATA: { int[][][] values = new int[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE]; @@ -116,7 +114,7 @@ public class ClientTerrainManager { } } } - ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ(), ChunkData.NO_STRIDE); + ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ(), ChunkData.NO_STRIDE, ChunkData.NOT_HOMOGENOUS); data.setVoxelType(values); data.setVoxelWeight(weights); terrainCache.addChunkDataToCache( @@ -138,26 +136,27 @@ public class ClientTerrainManager { } IntBuffer intView = buffer.asIntBuffer(); intView.position(floatBuffer.position()); + int firstType = -1; + boolean homogenous = true; for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){ for(int y = 0; y < ChunkData.CHUNK_SIZE; y++){ for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){ values[x][y][z] = intView.get(); + if(firstType == -1){ + firstType = values[x][y][z]; + } else if(homogenous && firstType == values[x][y][z]){ + homogenous = false; + } } } } - ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ(), message.getchunkResolution()); + ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ(), message.getchunkResolution(),homogenous ? firstType : ChunkData.NOT_HOMOGENOUS); data.setVoxelType(values); data.setVoxelWeight(weights); terrainCache.addChunkDataToCache( message.getworldX(), message.getworldY(), message.getworldZ(), data ); - if(message.getworldX() == 16 && message.getworldY() == 0 && message.getworldZ() == 32){ - System.out.println("Received! " + message.getchunkResolution()); - for(int i = 0; i < 10; i++){ - System.out.println(values[1][i][3]); - } - } //remove from request map this.requestedMap.remove(this.getRequestKey(message.getworldX(), message.getworldY(), message.getworldZ(), message.getchunkResolution())); } break; @@ -166,6 +165,7 @@ public class ClientTerrainManager { break; } } + messageQueue.clear(); for(TerrainMessage message : bouncedMessages){ messageQueue.add(message); } diff --git a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java index 3067161f..031bae7b 100644 --- a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java +++ b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java @@ -56,9 +56,8 @@ public class ImGuiChunkMonitor { ImGui.text("Chunk Monitor"); Globals.clientDrawCellManager.updateStatus(); - ImGui.text("Renderable chunks: " + Globals.clientDrawCellManager.getGenerated()); - ImGui.text("Full res chunks: " + Globals.clientDrawCellManager.getMaxResCount()); - ImGui.text("Half res chunks: " + Globals.clientDrawCellManager.getHalfResCount()); + // ImGui.text("Full res chunks: " + Globals.clientDrawCellManager.getFullResCacheSize()); + // ImGui.text("Garbage queue: " + Globals.clientDrawCellManager.getGarbageSize()); //client network pressure diff --git a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiMemory.java b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiMemory.java new file mode 100644 index 00000000..b39c6d41 --- /dev/null +++ b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiMemory.java @@ -0,0 +1,105 @@ +package electrosphere.client.ui.menu.debug; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.List; + +import electrosphere.engine.Globals; +import electrosphere.renderer.ui.imgui.ImGuiLinePlot; +import electrosphere.renderer.ui.imgui.ImGuiLinePlot.ImGuiLinePlotDataset; +import electrosphere.renderer.ui.imgui.ImGuiWindow; +import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback; +import imgui.ImGui; + +/** + * Debug menu for memory profiling + */ +public class ImGuiMemory { + + /** + * Size of a kilobyte + */ + static final long KB = 1024; + + /** + * Size of a megabyte + */ + static final long MB = 1024 * 1024; + + /** + * Size of a gigabyte + */ + static final long GB = 1024 * 1024 * 1024; + + /** + * Number of points on the graph + */ + static final int GRAPH_POINT_COUNT = 50; + + //window for viewing information about the memory usage + protected static ImGuiWindow memoryWindow; + + /** + * Creates the windows in this file + */ + protected static void createMemoryWindows(){ + createMemoryDebugWindow(); + } + + /** + * Memory window + */ + protected static void createMemoryDebugWindow(){ + + //memory usage graph + ImGuiLinePlot memoryGraph = new ImGuiLinePlot("Memory Usage",400,400); + ImGuiLinePlotDataset memoryGraphDataset = new ImGuiLinePlotDataset("Memory Usage (mb)", GRAPH_POINT_COUNT); + memoryGraphDataset.zeroOut(); + memoryGraph.addDataset(memoryGraphDataset); + + memoryWindow = new ImGuiWindow("Memory Usage"); + memoryWindow.setCallback(new ImGuiWindowCallback() { + @Override + public void exec() { + + //get garbage collector name + List gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans(); + if(gcMxBeans != null && gcMxBeans.size() > 0){ + GarbageCollectorMXBean gcMxBean = gcMxBeans.get(0); + ImGui.text(gcMxBean.getName() + " - " + gcMxBean.getObjectName()); + } + + //get memory usage + long totalMemory = Runtime.getRuntime().totalMemory(); + long freeMemory = Runtime.getRuntime().freeMemory(); + long memoryUsage = totalMemory - freeMemory; + ImGui.text("Total Memory: " + formatMemory(totalMemory)); + ImGui.text("Free Memory: " + formatMemory(freeMemory)); + ImGui.text("Memory Usage: " + formatMemory(memoryUsage)); + + //memory usage graph + memoryGraphDataset.addPoint(memoryUsage); + memoryGraph.draw(); + + } + }); + memoryWindow.setOpen(false); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(memoryWindow); + } + + /** + * Formats a memory value + * @param memoryRaw The memory value + * @return The formatted string + */ + private static String formatMemory(long memoryRaw){ + if(memoryRaw < KB){ + return "" + memoryRaw; + } else if(memoryRaw < MB){ + return (memoryRaw / KB) + "kb"; + } else { + return (memoryRaw / MB) + "mb"; + } + } + +} diff --git a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java index 7cb463b5..c1c338c7 100644 --- a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java +++ b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java @@ -53,6 +53,7 @@ public class ImGuiWindowMacros { ImGuiTestGen.createTestGenWindows(); ImGuiChunkMonitor.createChunkMonitorWindows(); ImGuiGriddedManager.createGriddedManagerWindows(); + ImGuiMemory.createMemoryWindows(); } /** @@ -114,10 +115,11 @@ public class ImGuiWindowMacros { fluidWindow.setCallback(new ImGuiWindowCallback() { @Override public void exec() { + ServerFluidManager fluidManager = Globals.playerManager.getPlayerRealm(Globals.clientPlayer).getServerWorldData().getServerFluidManager(); //audio engine details ImGui.text("Fluids Debug"); + ImGui.text("State: " + (fluidManager.getSimulate() ? "on" : "off")); if(ImGui.button("Toggle Simulation")){ - ServerFluidManager fluidManager = Globals.playerManager.getPlayerRealm(Globals.clientPlayer).getServerWorldData().getServerFluidManager(); fluidManager.setSimulate(!fluidManager.getSimulate()); } } @@ -193,6 +195,10 @@ public class ImGuiWindowMacros { if(ImGui.button("Gridded Data Cell Monitor")){ ImGuiGriddedManager.griddedManagerWindow.setOpen(true); } + //memory usage + if(ImGui.button("Memory Usage")){ + ImGuiMemory.memoryWindow.setOpen(!ImGuiMemory.memoryWindow.isOpen()); + } //close button if(ImGui.button("Close")){ mainDebugWindow.setOpen(false); diff --git a/src/main/java/electrosphere/collision/CollisionEngine.java b/src/main/java/electrosphere/collision/CollisionEngine.java index 8c31840b..b661179f 100644 --- a/src/main/java/electrosphere/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/collision/CollisionEngine.java @@ -722,10 +722,10 @@ public class CollisionEngine { //make uncollidable if(PhysicsEntityUtils.containsDBody(e)){ DBody rigidBody = PhysicsEntityUtils.getDBody(e); - deregisterCollisionObject(rigidBody,PhysicsEntityUtils.getCollidable(e)); + this.deregisterCollisionObject(rigidBody,PhysicsEntityUtils.getCollidable(e)); e.removeData(EntityDataStrings.PHYSICS_COLLISION_BODY); if(rigidBody != null){ - deregisterPhysicsObject(rigidBody); + this.deregisterPhysicsObject(rigidBody); } } if(ServerPhysicsSyncTree.hasTree(e)){ diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 28d200d1..f2643a53 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -154,6 +154,10 @@ public class Globals { //set to true to trigger full GC every frame //a full GC includes collecting old generations as well -- likely very laggy!! public static boolean EXPLICIT_GC = false; + /** + * Number of frames to wait before triggering gc again + */ + public static final int GC_FRAME_FREQUENCY = 15; // diff --git a/src/main/java/electrosphere/engine/Main.java b/src/main/java/electrosphere/engine/Main.java index 35b4461e..cc78673a 100644 --- a/src/main/java/electrosphere/engine/Main.java +++ b/src/main/java/electrosphere/engine/Main.java @@ -376,7 +376,7 @@ public class Main { /// G A R B A G E C H E C K /// Globals.profiler.beginCpuSample("gc"); - if(Globals.EXPLICIT_GC){ + if(Globals.EXPLICIT_GC && Globals.timekeeper.getNumberOfRenderFramesElapsed() % Globals.GC_FRAME_FREQUENCY == 0){ System.gc(); } Globals.profiler.endCpuSample(); diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java index c3a3410a..893c7c77 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java @@ -41,7 +41,7 @@ public class TerrainChunk { * @return The terrain chunk entity */ public static Entity clientCreateTerrainChunkEntity(TransvoxelChunkData chunkData, int levelOfDetail, VoxelTextureAtlas atlas, boolean hasPolygons){ - Globals.profiler.beginCpuSample("TerrainChunk.clientCreateTerrainChunkEntity"); + Globals.profiler.beginAggregateCpuSample("TerrainChunk.clientCreateTerrainChunkEntity"); Entity rVal = EntityCreationUtils.createClientSpatialEntity(); diff --git a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java index 3dfa4b61..50b1f32b 100644 --- a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java @@ -45,7 +45,7 @@ public class EntityProtocol implements ClientProtocolTemplate { @Override public void handleSyncMessage(EntityMessage message) { - Globals.profiler.beginCpuSample("EntityProtocol.handleEntityMessage"); + Globals.profiler.beginAggregateCpuSample("EntityProtocol.handleEntityMessage"); LoggerInterface.loggerNetworking.DEBUG_LOOP("Parse entity message of type " + message.getMessageSubtype()); //error check diff --git a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java index 9782670a..1bf0eb1b 100644 --- a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java @@ -28,7 +28,7 @@ public class TerrainProtocol implements ClientProtocolTemplate { @Override public void handleSyncMessage(TerrainMessage message) { - Globals.profiler.beginCpuSample("TerrainProtocol.handleTerrainMessage"); + Globals.profiler.beginAggregateCpuSample("TerrainProtocol.handleTerrainMessage"); switch(message.getMessageSubtype()){ case RESPONSEMETADATA: Globals.clientWorldData = new ClientWorldData( diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index 009c0630..8064ff26 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -3,7 +3,7 @@ package electrosphere.server.terrain.generation; import java.util.HashMap; import java.util.Map; -import org.graalvm.polyglot.Value; +// import org.graalvm.polyglot.Value; import electrosphere.engine.Globals; import electrosphere.game.data.biome.BiomeData; @@ -94,9 +94,6 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];; int[][][] values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - if(worldX == 16 && worldY == 0 && worldZ == 32){ - System.out.println(worldX + " " + worldY + " " + worldZ); - } try { //actual generation algo @@ -129,34 +126,34 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { } if(this.useJavascript){ - Globals.scriptEngine.executeSynchronously(() -> { - Value getVoxelFunc = Globals.scriptEngine.invokeEngineMember("chunkGeneratorManager", "getVoxelFunction", SCRIPT_GEN_TEST_TAG); - for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); - for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ - for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - Value result = getVoxelFunc.execute( - finalWorldX, finalWorldY, finalWorldZ, - finalChunkX, finalChunkY, finalChunkZ, - stride, - heightfield[x][z], - surfaceBiome - ); - if(result != null){ - weights[x][y][z] = result.getMember("weight").asFloat(); - values[x][y][z] = result.getMember("type").asInt(); - } - } - } - Globals.profiler.endCpuSample(); - } - }); + // Globals.scriptEngine.executeSynchronously(() -> { + // Value getVoxelFunc = Globals.scriptEngine.invokeEngineMember("chunkGeneratorManager", "getVoxelFunction", SCRIPT_GEN_TEST_TAG); + // for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + // Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); + // for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ + // for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ + // int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + // int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + // int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + // int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + // int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + // int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + // Value result = getVoxelFunc.execute( + // finalWorldX, finalWorldY, finalWorldZ, + // finalChunkX, finalChunkY, finalChunkZ, + // stride, + // heightfield[x][z], + // surfaceBiome + // ); + // if(result != null){ + // weights[x][y][z] = result.getMember("weight").asFloat(); + // values[x][y][z] = result.getMember("type").asInt(); + // } + // } + // } + // Globals.profiler.endCpuSample(); + // } + // }); } else { for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java index 4467cd7c..e7fbf1b7 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java @@ -19,7 +19,7 @@ public class ServerChunkCache { /** * Number of chunks to cache */ - static final int CACHE_SIZE = 2500; + static final int CACHE_SIZE = 1500; /** * The size of the cache @@ -124,6 +124,14 @@ public class ServerChunkCache { queryRecencyQueue.add(0, key); Map cache = this.getCache(stride); cache.put(key, chunk); + while(queryRecencyQueue.size() > cacheSize){ + Long oldKey = queryRecencyQueue.remove(queryRecencyQueue.size() - 1); + cacheMapFullRes.remove(oldKey); + cacheMapHalfRes.remove(oldKey); + cacheMapQuarterRes.remove(oldKey); + cacheMapEighthRes.remove(oldKey); + cacheMapSixteenthRes.remove(oldKey); + } lock.release(); } diff --git a/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java b/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java index 177b2e89..deb68750 100644 --- a/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java +++ b/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java @@ -38,6 +38,7 @@ public class ClientDrawCellManagerTests { int worldDiscreteSize = 64; Globals.clientWorldData = new ClientWorldData(new Vector3f(0), new Vector3f(worldDiscreteSize * ServerTerrainChunk.CHUNK_DIMENSION), 0, worldDiscreteSize); ClientDrawCellManager manager = new ClientDrawCellManager(null, 64); + double precomputedMidDist = 0; Vector3d playerPos = new Vector3d(0,0,0); FloatingChunkTreeNode node = FloatingChunkTreeNode.constructorForTests(manager.chunkTree, 1, new Vector3i(16,0,0), new Vector3i(32,16,16)); node.convertToLeaf(DrawCell.generateTerrainCell(new Vector3i(0,0,0)));