The need for "smart" resolution-aware atlases

Hi there,

I’m trying to tackle the problem of intelligently loading image assets according to window resolution. I’ve read some of the threads on this forum already, but none of them seem to provide a full solution to the problem, at least, not one that meets all of my criteria.

My criteria are:

  • Must support standard def (sd), high def (hd), and double hight def (hd2) screens
  • I will export 3 pngs at 3 sizes, and the correct one must be loaded and used, depending on the window size
  • The pngs that are not chosen must not be loaded into memory (eg: older devices with small screens and therefore less memory should not be loading enormous background images intended for hd2 devices)

I’ve only been able to come up with one solution to this problem in Defold. But I think you’ll agree that my solution is overly complex, and would be far too much work if I had to do this for every sprite used in a game.

As a small test case, I’ve written a “smart background object” called background.go that shows a backdrop for the game. The backdrop image is dynamicaly loaded according to my criteria as I described above. The end result is that this backdrop image looks pixel perfect, regardless of window resolution and aspect ratio.

To achive this, I have written a render script such that:

  • The game operates in a standard “logical resolution”, regardless of window size and aspect (my “logical resolution” is the resolution specified in the project config)
  • The ortho projection for the scene is essentially the logical width and logical height of the scene, however, it can capture slightly more or less width and height to compensate for different window aspect ratios. The logical positions of objects within the scene do not change.

The smart background object (background.go) and the various resources necessary to make this possible look like this:

Game objects:

background.go
	- collection proxy ("background-collection-sd")
	- collection proxy ("background-collection-hd")
	- collection proxy ("background-collection-hd2")
	- script ("background.lua")

Collections:

collection ("background-collection-sd")
	- go (scale = 2)
		- sprite (atlas = "background-atlas-sd")

collection ("background-collection-hd")
	- go (scale = 1)
		- sprite (atlas = "background-atlas-hd")

collection ("background-collection-hd2")
	- go (scale = 0.5)
		- sprite (atlas = "background-atlas-hd2")

Atlases:

atlas ("background-atlas-sd")
	- image ("background-sd.png")

atlas ("background-atlas-hd")
	- image ("background-hd.png")

atlas ("background-atlas-hd2")
	- image ("background-hd2.png")

Scripts:

background.lua
	- dynamically loads one of the three collection proxies according to the resolution of the window.

So, in summary, this works like a charm. It meets all of my criteria. Except it’s a lot of bloody work: 3 images, 3 atlases, 3 collections with sprites at different scales, and an object with 3 collection proxies and a script to manage the dynamic loading. All of that just to get a sprite on the screen that’s of an appropriate resolution, without wasting lots of memory? It’s just too much.

I very much think that this is a problem that the Defold engine could help us out with. Obviously I don’t know too much about the internals of the engine, but to me, it seems that the atlas is the natural level at which to introduce some intelligence. For instance, what if the atlas could have 3 possible source images (at 3 different resolutions)? It would also need to lie about the sizes of the images, so that a sprite that uses the atlas would have the same dimensions regardless of whether it’s using low, medium, or high res image data.

One open question that would need to be addressed is: Who would make the decision about which image source to use? The engine? The developer? And when would this decision be made? As the engine starts up? Dynamically at runtime by a script?

Anyway, I think being able to make efficient use of the memory on a mobile device is pretty important, so I don’t think the prevalent advice of “just use a giant image, and let opengl downsample it for you” is going to cut it, particularly for large background images which get up-sized to the nearest power of 2.

Hopefully that provides some food for thought. Thanks for reading, and thanks for all your hard work on this great engine!

6 Likes

Actually, as this percolates in my mind a bit -

What about introducing a system much like your display profiles for the gui? The dev could define different screen profiles, and as the engine starts up, it would match the device against the most appropriate profile.

Atlases could have different image sets for each possible screen profile. As the atlas is first loaded, it would select which images to use based on which screen profile the engine chose at startup…

This way, it’s still very data-driven, and static.

1 Like

I agree with you. This is a very important feature especially for old or budget devices.

I tried to solve this problem using my own manager and this method of texture loading. But it’s a lot of work and bunch of issues in this realisation.

7 years ago I worked in one company where we made a CI system that builds resources with different resolution and engine that cas use all this textures. But it was Adobe AIR and we had more access to resource loading into the engine.

This issue already solved in Starling framework (for adobe flash/air) : http://wiki.starling-framework.org/manual/multi-resolution_development (screen coordinates in the abstract points and developer can choose wcith content scale factor he want use in the game and for a texture loading)

In theory this feature can be implemented using mipmaps where developer can chose of mipmap level for texture in the fragment shader depend of screen size. But mipmap can’t be x1.5 or x0.75.

I hope that this feature will be implemented in Defold.

2 Likes