Increasing load speed of CesiumURLTemplateRasterOverlay with local data

I’m trying to use CesiumUrlTemplateRasterOverlay to load local data using a localhost url: http://localhost:8080/tiles/16/{z}/{x}/{y}.png
The 16 in this example is an hour, and I have data for every hour from 12-17.
I can change the data shown by changing the hour in the URL in UE editor, which refreshes the data and collects the data for a new specified hour. e.g. http://localhost:8080/tiles/12/{z}/{x}/{y}.png.

It is working as intended, but I would like the data load to be more instant, such that the user could change the hour with a slider and see a more instant change.

Settings:
Projection: Geographic
Specify Tiling Scheme: false
Minimum level: 12
Maximum level: 16
I have generated the corresponding folder structure for level 12-16.
I have tried tile size 256 and 512, but they seem to load equally fast.
It does not look like Maximum Simultaneous Tile Loads changes much either.

From what I can tell, the whole terrain is reloaded. This might be a contributor, since I’m using Google Photorealistic 3D Tiles.

I also get a lot of errors such as:

Received response code 404 for image http://localhost:8080/tiles/12/4/24/14.png.

Which I find strange, since the 4 in this case would mean its trying to load Z level 4, even if I defined minimum to be 12.

Any ideas for load time improvements?

Regards, Gustav

Hi @Simstad,

Changing the URL on the overlay triggers a refresh, which removes the overlay from the tileset and adds it back (at runtime you’d change the URL and call Refresh on the overlay yourself). I believe the geometry stays loaded, since it only reloads when the overlay’s projection is new to the tiles. But the refresh throws away every loaded tile’s overlay texture and re-requests every source image. On top of that, Cesium for Unreal stitches each geometry tile’s texture from all the source tiles that overlap it, up to Maximum Texture Size / tile size per axis. With the defaults (2048 and 256) that means a single Google Photorealistic 3D Tiles tile can pull up to 64 images. Across all the loaded tiles that adds up to a lot of images to fetch and decode again, which is the delay you’re seeing. That also explains why tile size didn’t change much. The pixel budget per tile is roughly the same either way.

Level 4 requests: After picking the level from screen-space error and clamping it to your Minimum and Maximum Level, I think cesium-native checks whether the source tiles needed for a geometry tile would exceed Maximum Texture Size, and if so it drops the level until they fit. That step doesn’t consult Minimum Level, so a large, distant Google tile can end up asking for level 4. When that 404s there is no parent fallback below your minimum, so that geometry tile just gets no overlay. Those are far-away tiles anyway (x=24 at level 4 in the geographic scheme is around 90 degrees east), so I’d treat the 404s as log noise. If the spam bothers you, have the server return a transparent PNG for missing tiles instead. I think Minimum Level should be respected in that step, and I’ll raise it with the team.

Some ideas for faster switching: Since the cost is re-fetching, the goal is to avoid the fetch or hide it. A few directions, none of which I have tried on this exact setup:

  • Send cache headers from your server. Cesium for Unreal keeps an on-disk request cache, and it stores responses that carry Cache-Control: max-age (or a future Expires). If your local server sends something like Cache-Control: max-age=31536000, the second switch to a given hour comes from disk with no round trip. Decode and GPU upload still happen, but the server drops out of the loop. This is the cheapest thing to try first.
  • Hide the reload behind extra overlays. Add two or three CesiumUrlTemplateRasterOverlay components with different Material Layer Keys (the default material supports Overlay0 to Overlay2), keep the current hour visible, refresh the next hour on a hidden layer, then flip which layer shows in the material. That needs a custom tileset material; the material editing tutorial covers the setup. Each extra overlay costs its own textures, so VRAM goes up accordingly.
  • Raise the overlay’s Maximum Screen Space Error. Fewer pixels per refresh means a faster refresh, at the cost of overlay sharpness. I’d leave Maximum Texture Size alone, since lowering it makes the level-drop issue above happen more often.

One question, what is serving the tiles on localhost:8080? If it’s something single-threaded like Python’s built-in http.server, requests get serialized regardless of Maximum Simultaneous Tile Loads, and swapping in a static file server that handles concurrent requests (nginx, Caddy, or similar) may be the single biggest win.

Cheers!

Hi! Thank you for the detailed answer. I have had some time to test now, and I can confirm that moving from python http.server that I was using at first to Caddy helped a lot!
This is the caddyfile I use:

http://localhost:8080 {
   root \* my\file\path\to\tiles

   header {
       Cache-Control "public, max-age=31536000, immutable"
   }
   file_server
}

The change seems to be more or less instant now, and I have implemented a slider to change the hour part of the URL.

The main issue now seems to be that the flicker that occurs when the terrain reloads. It kinda breaks the visual of scrolling through the data.

My next step is to try the Overlay hack that you suggested. I will get back on that part once I have tested it.

I have now implemented a solution using an extended overlay material, and it is working great!

I added 6 CesiumUrlTemplateRasterOverlay components to my tileset, one for each hour 12-17, and pointed them to the url of the corresponding hour.

I then set the material layer key to Overlay0 - Overlay5, increasing the index for each component.

This of course meant that I needed a custom MI_CesiumThreeOverlaysAndClipping material, that implements extra layers up to Overlay5.

With that in place, I also implemented a custom ML_CesiumRasterOverlay that checks a scalar parameter in a MaterialParameterCollection, CurrentHour, against its own hour parameter, which I can then edit for each layer in my material. If the layer hour and CurrentHour match, I mutliply the opactity by 1, and otherwise by 0.

I then use my slider to change the CurrentHour parameter and the result is instant switching between datasets!

Of course, I guess there is some performance loss with using 6 components at once, but It doesnt seem to be a problem at least on my computer. Together with the fast load times from using Caddy, it still loads pretty fast if I want to swap to another collection of hours. That is acceptable in our use case, it was just between hours that needed to be instant.

Thanks for all the help!