Skip to content

Add configurable object streaming limits (engineSetObjectStreamingLimits)#5055

Open
IOrosa wants to merge 1 commit into
multitheftauto:masterfrom
IOrosa:configurable-object-streaming-limits
Open

Add configurable object streaming limits (engineSetObjectStreamingLimits)#5055
IOrosa wants to merge 1 commit into
multitheftauto:masterfrom
IOrosa:configurable-object-streaming-limits

Conversation

@IOrosa

@IOrosa IOrosa commented Jul 13, 2026

Copy link
Copy Markdown

What this does

Implements the configurable limits discussed in #5054.

On maps with a lot of custom-object decoration (e.g. race maps placing thousands of .map
objects), decoration stops loading in dense areas and the world flickers. The cause is a
hard cap of 500 high-detail streamed-in objects (CClientObjectManager sets
m_uiMaxStreamedInCount = MAX_OBJECTS_MTA / 2, with MAX_OBJECTS_MTA = 1000), combined
with the fixed 1000-entry render lists that flicker when exceeded.

This PR makes those limits opt-in and configurable, with defaults equal to current
behaviour (fully backward compatible):

New client functions

  • bool engineSetObjectStreamingLimits(int highDetail, int lowLod) — how many high-detail
    (near) and low-LOD (far) objects may stream in at once. Returns false if invalid or the
    total exceeds the object pool.
  • int, int engineGetObjectStreamingLimits() — current limits.

Defaults stay at 500 / 500 (stock), so servers that don't call these are unaffected.

Supporting engine changes

  • Render list capacity: relocate CRenderer::ms_aVisibleEntityPtrs (0xB75898) and
    ms_aVisibleLodPtrs (0xB748F8) to larger buffers and raise the Check_NoOfVisible*
    clamps. This lets the extra objects actually render instead of flickering (a long-standing
    ask, New Limit Adjuster #869). It is inert with the default limits — a scene rarely exceeds 1000 visible
    entities — so it does not change stock behaviour.
  • Object pool ceiling: MAX_OBJECTS_MTA raised to act as the maximum the runtime limits
    may reach; node pools scale with it.

Why (draw-call trade-off)

Raising these trades FPS for density, so it must be the server/mapper's informed choice —
that's why it's opt-in rather than a global bump. I profiled FPS vs. rendered-object count
while driving a dense map (single machine, DX9, ~52 FPS frame limiter):

Rendered objects (high + low-LOD) FPS
0 – 1500 52
1750 – 2000 46
2000 – 2250 43
2500 – 2750 33
3000+ 30

Linear fit: ~9.4 FPS lost per 1000 rendered objects, flat at 52 up to ~1800. Low-LOD
objects cost the same as high-detail ones despite far fewer polys, i.e. this is draw-call
bound (CPU/DX9), consistent with #905. On my content, engineSetObjectStreamingLimits(1500, 800)
gave a flat ~52 FPS with ~2x the object density of stock.

Backward compatibility

  • Defaults are identical to current behaviour (500 / 500).
  • The render-array relocation and larger pools are inert unless the limits are raised.
  • No new ACL rights; no server config changes.

Testing

Built and tested locally on a dense custom race map: /start-ing a resource that calls
engineSetObjectStreamingLimits(1500, 800) visibly loads far more decoration; changing the
values at runtime updates streaming immediately; stopping it restores stock (500/500). No
crashes; FPS matches the table above.

Open questions for reviewers

  1. Pool memory: the object pool ceiling (MAX_OBJECTS_MTA) is compile-time, so raising
    it costs some memory for everyone even at default limits. Happy to tune the ceiling, make
    it configurable, or explore growing the pool on demand — guidance welcome.
  2. API surface: free engine* functions here; would you also like OOP methods on an
    Engine class, and/or a getter for current streamed-in counts to help scripts auto-tune?
  3. Wiki docs to follow once the API shape is agreed.

@IOrosa IOrosa force-pushed the configurable-object-streaming-limits branch from 3b52f04 to 512df42 Compare July 13, 2026 20:30
…its)

Dense custom maps hit a hard cap of 500 high-detail streamed-in objects
(CClientObjectManager: MAX_OBJECTS_MTA / 2), so decoration stops loading in
busy areas even though there is memory to spare. Expose the streaming caps as
opt-in client functions, defaulting to stock behaviour:

  - engineSetObjectStreamingLimits(highDetail, lowLod)
  - engineGetObjectStreamingLimits()

Also relocate the fixed 1000-entry visible-entity / visible-LOD render arrays
(CRenderer::ms_aVisibleEntityPtrs / ms_aVisibleLodPtrs) to larger buffers and
raise the Check_NoOfVisible* clamps, so the extra objects can render instead of
flickering. This is inert with the default limits and does not change stock
behaviour (a scene rarely exceeds 1000 visible entities). Related: multitheftauto#869.

Raises the object pool ceiling (MAX_OBJECTS_MTA) so the runtime limits may be
raised; node pools scale with it. Default streaming stays at 500/500, so the
change is fully backward compatible.

Refs multitheftauto#5054
@IOrosa IOrosa force-pushed the configurable-object-streaming-limits branch from 512df42 to 265a4f3 Compare July 13, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant