I have a scene updater that dynamically loads/unloads map tiles around a subject, as it moves through the world. The tiles are PNGs, no alpha, stored on disk. There are over 10,000 tiles, and the subject would typically cross tile boundaries every ~10 seconds. I keep 9 tiles loaded around the subject at any time in my Android app, but the problem is growing native memory in Android profiler (the problem may not be android-specific, just surfaces easier in a more memory-constrained device). What follows is potentially a crash (out of memory), black tiles showing randomly at runtime, etc.
So I thought I should avoid allocating memory every at boundary crossing and instead change the underlying image data at runtime, on a pool of geometries that I initialized:
val texture = assetManager.loadTexture("tile1.png")?.apply {
anisotropicFilter = 4
}
val geometry = Geometry("tile_$i").apply {
mesh = Quad(quadSize, quadSize) // actually, the PNGs are 1024x1024
material = Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md")
.apply {
setTexture("ColorMap", texture)
}
}
// add `geometry` to pool...
which repeats for the pool size. Even without the pool, I was loading the PNGs in the same way except I would create a new Geometry/Texture every time. Instead, I now keep references to 9 Geometry objects along with their textures and at runtime change the image:
assetManager.locateAsset(TextureKey("the_new_tile_to_load.png"))
?.let { image ->
recycledTexture.image = image
}
// repeat for the 9 tiles
// re-position Geometry objects around the subject as needed
But I still get OutOfMemory eventually, presumably because the old texture.image
data would still be allocated? (And on Android some of the images loaded this way won’t even show… seems to work on desktop though).
What am I missing here, and would this be the right approach to load/unload the PNGs? Given I can load the new image content as a ByteArray, when the subject crosses a tile boundary, how can I re-use the previously-allocated bytebuffer and set the new image data?
Would I instead tackle this differently and try to deallocate the native memory?
Thanks in advance.
Memory profiler - native memory increasing over time: