Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -268,23 +266,27 @@ public GlBuffer getBufferObject() {
return this.arenaBuffer;
}

public boolean upload(CommandList commandList, Stream<PendingUpload> stream) {
public boolean upload(CommandList commandList, Collection<PendingUpload> 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<PendingUpload> queue = stream.collect(Collectors.toCollection(LinkedList::new));
List<PendingUpload> queue = new LinkedList<>(uploads);

// Try to upload all of the data into free segments first
this.tryUploads(commandList, queue);

// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ public void uploadChunks() {
this.needsUpdate = true;
}

private void processChunkBuildResults(ArrayList<ChunkBuildOutput> results) {
private void processChunkBuildResults(List<ChunkBuildOutput> results) {
var filtered = filterChunkBuildResults(results);

this.regions.uploadMeshes(RenderDevice.INSTANCE.createCommandList(), filtered);
Expand Down Expand Up @@ -447,7 +447,7 @@ private void updateSectionInfo(RenderSection render, BuiltSectionInfo info) {
}
}

private static List<ChunkBuildOutput> filterChunkBuildResults(ArrayList<ChunkBuildOutput> outputs) {
private static List<ChunkBuildOutput> filterChunkBuildResults(List<ChunkBuildOutput> outputs) {
var map = new Reference2ReferenceLinkedOpenHashMap<RenderSection, ChunkBuildOutput>();

for (var output : outputs) {
Expand All @@ -466,12 +466,19 @@ private static List<ChunkBuildOutput> filterChunkBuildResults(ArrayList<ChunkBui
return new ArrayList<>(map.values());
}

private ArrayList<ChunkBuildOutput> collectChunkBuildResults() {
ArrayList<ChunkBuildOutput> results = new ArrayList<>();
ChunkJobResult<ChunkBuildOutput> result;
private List<ChunkBuildOutput> collectChunkBuildResults() {
ChunkJobResult<ChunkBuildOutput> 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<ChunkBuildOutput> results = new ArrayList<>();

while (result != null) {
results.add(result.unwrap());
result = this.buildResults.poll();
}

return results;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,35 @@ public void update() {

public void uploadMeshes(CommandList commandList, Collection<ChunkBuildOutput> 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<ChunkBuildOutput> filterOutputs(List<ChunkBuildOutput> outputs, boolean indexOnly) {
List<ChunkBuildOutput> 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<ChunkBuildOutput> results) {
var uploads = new ArrayList<PendingSectionUpload>();

Expand Down Expand Up @@ -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<PendingUpload>(uploads.size());
var indexUploads = new ArrayList<PendingUpload>();

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
Expand Down Expand Up @@ -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<PendingUpload>(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
Expand Down
Loading