my code loads and disposes a lot of image tiles during run-time - something like google maps.
The new textures get loaded on demand based on camera location etc.
Now it can happen that the code fires 50 texture requests at once, and if the connection is fast enough they all come in within a few frames, which in turn leads to lag as they all get updated the same time.
What would be a good way to spread the loading? I looked at the imageloader, loadingmanager etc. but they all seem straightforward - no queues or anything like that.
While i know my way around graphic engines, i am very new to all the network stuff - is there anything in JavaScript where i can limit the number of concurrent requests, keeping the rest in the queue?
My plan for now would be to have an intermediate structure that keeps track of ongoing transfers, waiting for callbacks and making sure only a few are updated at a time. While this is doable, i wonder if there is something like that already implemented in the http protocol.
I guess that your problem is not in the “load” but in the decoding. I’m guessing you use some form of compressed image format, like png or jpeg. What the browser is going to do for you - is convert that into RGB or RGBA bitmap as appropriate and that takes time. Actual upload of the texture to the GPU is relatively fast. This is based on my own observations.
noticed the same, but with low enough request per time it works out…
I did not research much in that area - but it looks like every approach has its downsides:
compressed textures are bigger (bandwidth) have artifacts and are not supported everywhere… (do they need the same conversion as jpeg or can they be directly uploaded to the gpu?)
Jpeg need to be decoded and converted
some home-brew binary stream with zip compression that can be loaded as typed array?
are there better ways then jpeg/png when bandwidth is a factor?
Yeah, there’s always HTTP compression. It’s useful to check whether your bitmap might end up small enough when transferred via a compressed channel that PNG/JPEG encoding might not be necessary. I found that for low PNG compression together with HTTP compression texture size differences are quite small, at least in my use-cases.
For example, my entire game with models and textures and every other asset is about 60 Mb of HTTP traffic over a compressed channel. I rate that as quite acceptable.
Your hand-rolled activeRequests < maxRequests counter is exactly the
core of it — and with a cap of 3–4 the “50 requests land within a
few frames” burst can’t happen in the first place. If you’d rather
not own the edge cases, I extracted this scheduling problem into a
small library: image-pump (GitHub, npm — disclosure: I’m the
author).
What it adds over the counter:
a shared concurrency budget across all consumers (maxActivePumps);
deduplication: the same source is requested at most once,
everyone else subscribes to the result;
a backpressure pause: after a load that took longer than ~2 frames,
the slot stays busy for two more before the NEXT queued load
starts, so bursts don’t cascade;
a stall kill-switch (loadTimeoutMs) so one hung request can’t
occupy a slot forever.
For your tile case specifically: subscribe() when a tile enters, dropState() when you dispose it — disposed tiles stop driving loads
and in-flight ones finish silently, while kept tiles stay in the
cache for when the camera comes back. And since @Usnul is right that
decoding is the expensive part: the loader resolves only after img.decode(), so the decode happens off the critical path. Wrapping
the result into THREE.Texture stays on your side; a pluggable loader
(fetch/createImageBitmap) is on the roadmap.