Skip to content

Chunk generation order goes stale as the player moves #5361

Description

@soloturn

AI-assisted issue. Filed by agent driven by @soloturn via GDD.

Context

Found while reviewing #4879 and its base branch feat/chunk-ordering-reactor to decide whether either was still worth reviving. Filing this so the underlying problem is tracked independently of the PRs that tried to fix it — it has now outlived two of them.

This is not a newly discovered bug. It has a documented history, is still reproducible on develop, and the last serious attempt (#4822) is still open and was never rejected on technical grounds — it was reviewed favourably and simply stalled.

Problem / Current State

Chunk generation order goes stale as the player moves. The canonical description is @naalit's, from #4773:

You'll probably notice that chunks near you often generate after chunks far away from you, especially if you're moving. Right now, the ChunkProcessingPipeline orders chunks by storing them in a priority queue indexed by their distance to a player. The problem is, when the player moves the priority queue has cached some priorities, and so a chunk that used to be far away and is now close will still have a low priority.

In extreme cases, if you move to right next to a chunk that used to be at the edge of your view distance, all of the chunks that are now in range that didn't used to be will generate before the one right next to you.

Still accurate on current develop. The mechanism:

  1. RelevanceSystem eagerly calls chunkProvider.createOrLoadChunk(pos) for every position entering a relevance region (RelevanceSystem.java:137, :189).
  2. Each becomes a PositionFuture in ChunkProcessingPipeline's PriorityBlockingQueue(800, comparator), ordered by ChunkTaskRelevanceComparator (distance from region centres).
  3. That heap is ordered at insertion time. PriorityBlockingQueue only re-compares during sift operations, so entries already placed keep positions derived from scores computed when the player was somewhere else.

Related smell in the same file, untouched since 2021:

.sorted(new PositionRelevanceComparator()) //<-- this is n^2 cost. not sure why this needs to be sorted like this.

(RelevanceSystem.java:182)

Prior art — read this before starting

Three attempts, none merged:

#4773 closed feat: order chunks on demand. Original fix. Replaced the priority queue with a re-sorted list plus a new InitialChunkProvider that generator threads pull from. Closed in favour of the Reactor rewrite.
#4822 OPEN feat: rework chunk ordering using Reactor. Rewrite of #4773 using Reactor, per #4798. Reviewed by @keturn, @skaldarnar, @pollend, @DarkWeird; @skaldarnar recorded before/after comparison videos; @pollend asked "anything else blocking this?" — and it stopped there. Now conflicting.
#4879 draft Late light merging, stacked on #4822's branch. Separate idea, being pursued separately.

Umbrella context: #4798 (Refactor usage of concurrency with reactor) is still open with unchecked boxes; this work is part of it.

The design question that actually blocked #4822 is still unanswered. @keturn proposed the shape you would expect:

Flux<Chunk> fullChunks = locations.parallel().map(chunkProvider);
fullChunks.subscribe(cacheChunk);

@naalit's objection — "when there are no more chunks to generate right now and execution pauses, there's no way to wake it up again from the main thread" — is what the implemented version works around with notifyRelevanceChanged() (called on relevance-region update, i.e. roughly every player move). @keturn: "This is the part I keep getting stuck on when trying to think about this design."

Any new attempt should answer that explicitly rather than rediscover it.

Acceptance Criteria

  • Chunk generation order is recomputed against the player's current position rather than fixed when the chunk was first queued.
  • The number of chunk positions in flight is bounded, instead of every relevant position being submitted up front.
  • The n^2 sort in RelevanceSystem.addRelevanceEntity is gone or justified.
  • The wake-up/backpressure design above is settled and written down, not left implicit.
  • Measured: moving continuously in one direction at a large view distance generates nearest-first, verified by instrumenting generation order rather than by eye.

Technical Notes

2f05f3132 on feat/chunk-ordering-reactor is worth reading as a design reference even though it no longer applies. It inverts the flow to pull-based: RelevanceSystem stops creating chunks and only exposes neededChunks(); LocalChunkProvider.chunkFlux() is a Flux with backpressure; the pipeline requests CHUNKS_AT_ONCE at a time, and on each request the pending list is re-sorted by current relevance and only the nearest N handed over.

Reviving #4822 by rebasing is not viable — it predates the org.terasology.engine.* restructure (#5192) and conflicts on its first commit. Treat it as a specification, not a patch.

Constraints a replacement must respect that #4822 predates:

Two loose ends inherited from the old discussions:

Sequencing: this gets easier once light merging moves out of the pipeline. Light merging is currently the only multi-chunk stage (the sole production uses of ChunkTaskProvider.createMulti), and it is what forces the pipeline to hold many chunks in flight pending neighbours — the branch's processingInfoReactor() defer-loop exists largely to service exactly that. With it gone the pipeline is a straight per-chunk sequence and a bounded pull model is considerably simpler.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    Category: PerformanceRequests, Issues and Changes targeting performanceSize: LVery big effort likely requiring a lot of research and work in many areas across the codebaseTopic: ArchitectureRequests, Issues and Changes related to software architecture, programming patterns, etc.Topic: ConcurrencyRequests, issues, and changes relating to threading, concurrency, parallel execution, etc.

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions