diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/GlBufferArena.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/GlBufferArena.java index 124519452..a89e78cc4 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/GlBufferArena.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/GlBufferArena.java @@ -11,8 +11,6 @@ import java.util.Collection; import java.util.LinkedList; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; public class GlBufferArena { static final boolean CHECK_ASSERTIONS = false; @@ -268,13 +266,13 @@ public GlBuffer getBufferObject() { return this.arenaBuffer; } - public boolean upload(CommandList commandList, Stream stream) { + public boolean upload(CommandList commandList, Collection uploads) { // Record the buffer object before we start any work // If the arena needs to re-allocate a buffer, this will allow us to check and return an appropriate flag GlBuffer buffer = this.arenaBuffer; // A linked list is used as we'll be randomly removing elements and want O(1) performance - List queue = stream.collect(Collectors.toCollection(LinkedList::new)); + List queue = new LinkedList<>(uploads); // Try to upload all of the data into free segments first this.tryUploads(commandList, queue); @@ -282,9 +280,13 @@ public boolean upload(CommandList commandList, Stream stream) { // If we weren't able to upload some buffers, they will have been left behind in the queue if (!queue.isEmpty()) { // Calculate the amount of memory needed for the remaining uploads - int remainingElements = (int)(queue.stream() - .mapToLong(upload -> upload.getDataBuffer().getLength()) - .sum() / this.stride); + long remainingBytes = 0; + + for (PendingUpload upload : queue) { + remainingBytes += upload.getDataBuffer().getLength(); + } + + int remainingElements = (int)(remainingBytes / this.stride); // Ask the arena to grow to accommodate the remaining uploads // This will force a re-allocation and compaction, which will leave us a continuous free segment diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/RenderSectionManager.java b/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/RenderSectionManager.java index eddb4bbee..80e2e8c1b 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/RenderSectionManager.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/RenderSectionManager.java @@ -403,7 +403,7 @@ public void uploadChunks() { this.needsUpdate = true; } - private void processChunkBuildResults(ArrayList results) { + private void processChunkBuildResults(List results) { var filtered = filterChunkBuildResults(results); this.regions.uploadMeshes(RenderDevice.INSTANCE.createCommandList(), filtered); @@ -447,7 +447,7 @@ private void updateSectionInfo(RenderSection render, BuiltSectionInfo info) { } } - private static List filterChunkBuildResults(ArrayList outputs) { + private static List filterChunkBuildResults(List outputs) { var map = new Reference2ReferenceLinkedOpenHashMap(); for (var output : outputs) { @@ -466,12 +466,19 @@ private static List filterChunkBuildResults(ArrayList(map.values()); } - private ArrayList collectChunkBuildResults() { - ArrayList results = new ArrayList<>(); - ChunkJobResult result; + private List collectChunkBuildResults() { + ChunkJobResult result = this.buildResults.poll(); - while ((result = this.buildResults.poll()) != null) { + // Avoid allocating a list on frames where no jobs have finished + if (result == null) { + return Collections.emptyList(); + } + + List results = new ArrayList<>(); + + while (result != null) { results.add(result.unwrap()); + result = this.buildResults.poll(); } return results; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/region/RenderRegionManager.java b/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/region/RenderRegionManager.java index 4ab96cb73..b3ce6d7d4 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/region/RenderRegionManager.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/region/RenderRegionManager.java @@ -50,11 +50,35 @@ public void update() { public void uploadMeshes(CommandList commandList, Collection results) { for (var entry : this.createMeshUploadQueues(results)) { - this.uploadMeshes(commandList, entry.getKey(), entry.getValue().stream().filter(o -> !o.isIndexOnlyUpload()).toList()); - this.uploadResorts(commandList, entry.getKey(), entry.getValue().stream().filter(ChunkBuildOutput::isIndexOnlyUpload).toList()); + this.uploadMeshes(commandList, entry.getKey(), filterOutputs(entry.getValue(), false)); + this.uploadResorts(commandList, entry.getKey(), filterOutputs(entry.getValue(), true)); } } + // Selects the outputs whose index-only flag matches. Either group is often empty, so the list is only + // allocated once an output matches. + private static List filterOutputs(List outputs, boolean indexOnly) { + List filtered = null; + + for (ChunkBuildOutput output : outputs) { + if (output.isIndexOnlyUpload() != indexOnly) { + continue; + } + + if (filtered == null) { + filtered = new ArrayList<>(); + } + + filtered.add(output); + } + + if (filtered == null) { + return Collections.emptyList(); + } + + return filtered; + } + private void uploadMeshes(CommandList commandList, RenderRegion region, Collection results) { var uploads = new ArrayList(); @@ -83,11 +107,20 @@ private void uploadMeshes(CommandList commandList, RenderRegion region, Collecti var resources = region.createResources(commandList); var geometryArena = resources.getGeometryArena(); - boolean bufferChanged = geometryArena.upload(commandList, uploads.stream() - .map(upload -> upload.vertexUpload)); + var vertexUploads = new ArrayList(uploads.size()); + var indexUploads = new ArrayList(); - bufferChanged |= resources.getIndexArena().upload(commandList, uploads.stream() - .map(upload -> upload.indexUpload).filter(Objects::nonNull)); + for (PendingSectionUpload upload : uploads) { + vertexUploads.add(upload.vertexUpload); + + if (upload.indexUpload != null) { + indexUploads.add(upload.indexUpload); + } + } + + boolean bufferChanged = geometryArena.upload(commandList, vertexUploads); + + bufferChanged |= resources.getIndexArena().upload(commandList, indexUploads); // If any of the buffers changed, the tessellation will need to be updated // Once invalidated the tessellation will be re-created on the next attempted use @@ -133,8 +166,13 @@ private void uploadResorts(CommandList commandList, RenderRegion region, Collect var resources = region.createResources(commandList); - boolean bufferChanged = resources.getIndexArena().upload(commandList, uploads.stream() - .map(upload -> upload.indexUpload).filter(Objects::nonNull)); + var indexUploads = new ArrayList(uploads.size()); + + for (PendingResortUpload upload : uploads) { + indexUploads.add(upload.indexUpload); + } + + boolean bufferChanged = resources.getIndexArena().upload(commandList, indexUploads); // If any of the buffers changed, the tessellation will need to be updated // Once invalidated the tessellation will be re-created on the next attempted use