How to use texture atlas correctly

Share Button

noteToday a one good man tried to optimize Starling application. He had up to 10 texture atlases that generate really huge amount of draw calls. I’m gonna help not only to him but also for anyone know don’t know this tricks.

Firstly we must create the atlas. I will use Adobe Flash CC 2014 for this:

flashcc2014atlas

Lets compile entry point class:

package  {

    import flash.display.Sprite;
    import starling.core.Starling;

    public class Main extends Sprite {        

        public function Main() {            
            var str : Starling = new Starling(BaseClass, stage);
            str.stage.color = 0xcccccc;
            str.start();
            str.showStats = true;
        }
    }

}

And now our base class:

package  {
	import starling.display.Sprite;
	import flash.display.BitmapData;
	import starling.textures.Texture;
	import starling.textures.TextureAtlas;
	import starling.display.Image;

	public class BaseClass extends Sprite{		
		private var atlasColorBitmapData : myAtlasColor;
		private var atlasXml : XML;		
		public function BaseClass() {			
			atlasColorBitmapData = new myAtlasColor();			
			atlasXml =			 
			<TextureAtlas imagePath="atlas.png">
			<!-- Created with Adobe Flash Professional version 14.1.0.97 -->
			<!-- http://www.adobe.com/products/flash.html -->
				<SubTexture name="one.png" x="1" y="1" width="62" height="62" pivotX="0" pivotY="0"/>
				<SubTexture name="two.png" x="1" y="64" width="62" height="62" pivotX="0" pivotY="0"/>
			</TextureAtlas>

			var textureColor : Texture = Texture.fromBitmapData( atlasColorBitmapData, false);
			var textureAtlasColor : TextureAtlas = new TextureAtlas(textureColor, atlasXml);

			var oneColor : Image = new Image( textureAtlasColor.getTexture("one.png"));
			var twoColor : Image = new Image( textureAtlasColor.getTexture("two.png"));

			oneColor.x = 200;
			oneColor.y = 100;

			twoColor.x = oneColor.x + oneColor.width * .25;
			twoColor.y = oneColor.y + oneColor.height * .25;

			addChild(oneColor);
			addChild(twoColor);			
		}
	}	
}

We got 1 draw calls as expected:

preview1

Now let’s try to recreate issue. Add one more Black/White atlas and add it as Image item into display:

preview2

Expectedly we got 2 draw calls because we have only 2 atlases that was used for this 4 items. In this step a lot of developers faced the challenge in the atlas usage. On the one hand we must have 2 draw calls using 2 atlases, on the other hand we can get unexpected increasing of draw calls.

Similar issues can be faced if you drawing textures from the atlas not in linear order but in mixed (oneColor > oneBW > twoColor > twoBW). As result we can see 4 draw calls instead of 2.

drawcallsextra

In this case you need to put oneColor + oneBW into atlas #1 and twoColor + twoBW into #2 so you can fix drawing order.

Most top mistakes of wrong atlas usage can be found in 2D tower defense games:
1) Ground and Health Bar placed on the atlas #1
2) Game Unit placed on the atlas #2

So when your game unit moving on the ground – GPU will process 3 draw calls instead of 2:
1) draw Ground from atlas #1
2) draw Unit from atlas #2
3) draw Health Bar from atlas #1

For reducing drw (draw call) in this example you can move Health Bar under the Unit so GPU will draw it with the Ground at once using 1 drw because it will use the same atlas and will be not overlapped by other textures from a different atlases. But this can looks ugly from the game art viewpoint.

To fix this you need just put the Health Bar to the same atlas #2 that you have used for the Unit so here is new render order:
1) draw Ground from atlas #1
2) draw Unit, Health Bar from atlas #2

Note:
If you have some free space in your atlas it’s not mean that you must use it for reducing atlas count. In some cases better to use 5 atlases instead of 2-3. But anyway try to keep atlas count at the minimum.

Here is the code that showing wrong atlas usage. Hope this helps. Also sorry for my English :)

package  {
	import starling.display.Sprite;
	import flash.display.BitmapData;
	import starling.textures.Texture;
	import starling.textures.TextureAtlas;
	import starling.display.Image;

	public class BaseClass extends Sprite{

		private var atlasColorBitmapData : myAtlasColor;
		private var atlasBWBitmapData : myAtlasBW;

		private var atlasXml : XML;

		public function BaseClass() {

			atlasColorBitmapData = new myAtlasColor();
			atlasBWBitmapData = new myAtlasBW();

			atlasXml =			 
			<TextureAtlas imagePath="atlas.png">
			<!-- Created with Adobe Flash Professional version 14.1.0.97 -->
			<!-- http://www.adobe.com/products/flash.html -->
				<SubTexture name="one.png" x="1" y="1" width="62" height="62" pivotX="0" pivotY="0"/>
				<SubTexture name="two.png" x="1" y="64" width="62" height="62" pivotX="0" pivotY="0"/>
			</TextureAtlas>

			var textureColor : Texture = Texture.fromBitmapData( atlasColorBitmapData, false);
			var textureAtlasColor : TextureAtlas = new TextureAtlas(textureColor, atlasXml);

			var textureBW : Texture = Texture.fromBitmapData( atlasBWBitmapData, false);
			var textureAtlasBW : TextureAtlas = new TextureAtlas(textureBW, atlasXml);

			var oneColor : Image = new Image( textureAtlasColor.getTexture("one.png"));
			var twoColor : Image = new Image( textureAtlasColor.getTexture("two.png"));

			oneColor.x = 100;
			oneColor.y = 50;

			twoColor.x = 120;
			twoColor.y = 70;

			var oneBW : Image = new Image( textureAtlasBW.getTexture("one.png"));
			var twoBW : Image = new Image( textureAtlasBW.getTexture("two.png"));

			oneBW.x = 140;
			oneBW.y = 90;

			twoBW.x = 160;
			twoBW.y = 110;

			addChild(oneColor);
			addChild(oneBW);
			addChild(twoColor);
			addChild(twoBW);
		}
	}

}
Share Button

This Post Has Been Viewed 2,644 Times

8 thoughts on “How to use texture atlas correctly

  1. chrust

    А как можно оптимизировать списки? Допустим, я делаю список контактов. Есть класс List, в котором несколько классов Item. Каждый экземпляр Item содержит 2 объекта – фон и текст. Причем текстура фона находится в одном атласе, а растровый шрифт в другом атласе. Я понимаю, что лучший вариант – объединить оба атласа, но по разным причинам не всегда это возможно или удобно. Так вот, при отрисовке списка мы будем постоянно переключаться с одного атласа на другой, увеличивая DRW calls. То есть для списка из 10 элементов я получаю не 2 DRW, как хотелось бы, а 20.

    Reply
    1. TheRabbit Post author

      Я советую в первую очередь посмотреть в сторону Feathers, если речь о Flash и Starling. Вот примеры http://feathersui.com/examples/components-explorer/

      Что же касается вопроса о DRW – я не вижу пути решения данного вопроса без совмещения в один атлас. В противном, придется мучиться и разносить элементы по слоям. И о вложенности фона и текста в контейнер надо забыть. Текст верхним слоем (отельным), фон – отдельным (нижним). Как только засунешь в контейнер текст и фон для удобства – сразу рост drw.

      Reply
      1. chrust

        Так и думал. Либо удобный код, но затратный для устройства, либо изворачивайся и потом вспоминай, зачем такое нагородил в коде. Спасибо за ответ.

        Reply
        1. TheRabbit Post author

          Я вообще не вижу проблем сувать атлас шрифта в атлас с текстурой. Можно ведь вообще компонент сделать отдельным атласом. Лучше получить +1 атлас и всего 2 drw, чем сделать весь ui отдельно от шрифта и получить 500 drw

          Reply
          1. chrust

            Да, можно даже разделить все атласы по окнам. То есть всё, что видно в данный момент на экране, вынести в отдельный атлас. И загружать его только когда надо, выгрузив все остальные атласы. При этом, конечно, могут повторяться текстуры шрифтов в разных атласах. Одни и те же шрифты могут использоваться в разных окнах. Но, думаю, не страшно получить незначительное увеличение файла приложения, если это положительно скажется на производительности.

            Reply
            1. TheRabbit Post author

              Я больше скажу. Можно создавать атласы на ходу. Как раз на одном из докалдов за это и говорил, что лучшее решение – предварительно готовое. Но иногда, когда иного пути нет – лучше этого бывает только создание на лету :)

              Т.е. зная заранее ассеты на уровне – мы загружаем их в память и генерим из них атлас. Тогда нам не надо будет создавать всевозможные вариации. Одно будет неизменным – блок с атласом шрифта.

              На самом деле это делается в течении рабочего дня и не отнимает кучу времени.

              Reply
  2. AlexFrost

    Для создания текстурных атласов с анимациями использовал
    https://github.com/grapefrukt/grapefrukt-export
    Слегка модифицировали его. Под него написан плеер. Достаточно удобно выходит.
    TheRabbit, может быть у вас есть в “багажнике” схожие библиотеки, которые могут облегчить жизнь? Посоветуете? :)

    Reply

Leave a Reply

Your email address will not be published.

Blue Captcha Image Refresh

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>