I profiled the software renderer to see where the time goes while zooming the main map.
On a Release SDL2 build with pak128.german, a developed 512x512 map and a 1280x1024
window, a zoom frame costs about 3.6x a normal frame, and the whole extra cost is sprite
rescaling in rezoom_img. GUI, scrolling, event batching, dirty-rectangle merging and
viewport traversal are already efficient and are not the bottleneck.
The reason is that each image keeps only one scaled buffer for the current zoom. When you
return to a zoom level you just used - which is exactly what rapid zooming and pinch
jitter do - the same visible sprites are scaled again from scratch.
The patch keeps one previous non-neutral scaled buffer per image. On a return to that
level the complete scaled buffer and its geometry are restored instead of running the
scaler again. Player-colour and day/night buffers are still regenerated the normal way,
so their existing invalidation behaviour is unchanged. A cache hit returns the exact
buffer a fresh rescale would have produced, so the result is identical.
Measured (same setup):
returning to a recently used level 18.50 ms -> 6.86 ms
sprites actually rescaled per frame about 340 -> 8.7
first visit to a new level unchanged (no caching benefit expected)
static redraw unchanged
scrolling unchanged
The secondary buffers are allocated lazily. On this map they added about 5 MiB of scaled
data plus roughly 1.2 MiB of fixed per-image metadata - around 1.3% of the current
scaled-image memory. Memory stayed bounded through 800 zoom transitions across all
levels, and the process shut down cleanly.
The product diff changes only simgraph16.cc (+45/-1): it adds the secondary slot, restores
it on a hit, initialises it during image registration, and frees it through the existing
bulk image cleanup. It keys on the image id (not a pointer, since the image array can be
reallocated), and it relies on the fact that a scaled buffer depends only on the source
image and the zoom factor - day/night and player colour live in separate buffers.
Correctness so far:
- the framebuffer was byte-for-byte identical with the cache enabled and disabled
throughout the full zoom test;
- I exercised the ground/climate texture regeneration path (which frees and re-registers
images): the secondary slots are freed correctly and the output stays identical;
- no crash, no leak, memory bounded.
I am posting this as a candidate rather than a finished patch: I have not been able to test
it on a real Android device yet (the code is in the shared renderer, so the path is the
same, but I would like on-device confirmation). It only helps when returning to a recently
used zoom level; it does not make the first visit to a new level faster. Feedback welcome,
especially from anyone who can try a pinch-zoom on Android.
I added the lazy cache 20 years ago. I never experienced any big lag on zooming since then. But as there is so much more main memory available, why not.
ALso you can pinch zoom on most laptop trackpad since years. I even have a touch display on my laptop. But I had no issues zoomin. Well, only that the zoom level is jsut change once per 20 ms or depending on frame rate when zoomed out.
Thanks, that historical context is helpful. I had not realised that the current lazy scaling cache dated back that far.
I agree that on a modern desktop the delay is usually not dramatic. What caught my attention was the measured difference when returning to a recently used level: on the pak128.german test case, the frame dropped from 18.5 ms to 6.86 ms because almost all repeated sprite scaling was avoided.
So I would describe this less as fixing broken zooming and more as using a small amount of additional memory to remove a measurable CPU spike. The first visit to a new level is unchanged, and the existing frame/event timing remains unchanged as well.
The additional cost in my test was about 5 MiB of lazy buffers plus around 1.2 MiB of metadata, with byte-identical output and bounded memory.
If the approach looks acceptable to you, I can keep the patch exactly as the small two-slot implementation attached, without extending it into a larger cache.
It may be my screen, but I did not notice any change with this patch. Pinch zoom works as before.
Thanks for testing it. That is useful feedback.
The patch is not expected to change the behaviour of pinch zoom, only the cost of returning to a scale level that was used recently. On the pak128.german test case that path was measurably faster, but if the difference is not perceptible on normal hardware, the additional per-image memory may not be justified.
I will treat this as an experimental optimisation rather than something that needs to be incorporated. I may do one final check on lower-end hardware or Android, where repeated sprite scaling could matter more, but otherwise I am happy to leave it here.
Indeed, the hardware was rather fast. My slow Android phone finally bit the dust.
Understood, thank you for trying it.
Without a slower Android device it is difficult to determine whether the measured reduction in repeated scaling work translates into a useful improvement on the hardware most likely to benefit from it.
Next week I expect to have a new PC ready with proper hardware-assisted Android virtualisation. That should let me run more representative and repeatable performance tests across constrained Android configurations and obtain better comparative metrics. I understand that this will not completely replace testing on real low-end hardware, but it should provide more useful evidence than the current desktop-only measurements.
The motivation behind this work is accessibility rather than optimising for its own sake. I do not want players to be left behind simply because they cannot afford more powerful hardware. In my view, a smooth and responsive core experience is more valuable than adding a hundred options that some players cannot comfortably use.
For now, I will leave the patch as an experimental optimisation. Once the Android test environment is ready, I will report the results, including the performance gain, memory cost and whether the difference is perceptible under constrained configurations. We can then decide whether the additional cache is justified.
Im my experience, the Android emulator on a PC is much faster than the real thing because screens tend to have less pixels and no thermal throttling.