Dear OpenFL Developers,
When starting to use Tilemaps, I noticed that whenever a Tilemap is attached to the DisplayList, it consumes a large amount of CPU time and also appears to continuously leak memory. I’ve tried to isolate the issue into a minimal test app (derived from the OpenFL Bunnymark example). The test application has two textfield “buttons”. Clicking these will attach or detach two Sprites, both of which have (unless otherwise specified) 1000 DisplayObject children. On the first Sprite, the children are all empty Tilemaps. On the second Sprite, the children are all empty Bitmaps (the second Sprite is used to check that the issue is not related to all DisplayObjects, only Tilemaps).
I have run the test program on Windows and Linux. On Linux, using Neko, when the program starts (‘openfl test neko’), memory usage is stable at 38 Mb and CPU utilization around 0-1 %. If the Bitmaps are attached, there is no detectable difference in CPU or memory usage. However, if the Tilemaps are attached CPU usage grows to 25% and memory usage starts to increase by around 50 Mb per second. Detaching the Tilemaps drops CPU usage back to 0-1% and memory usage does not increase any more, but the memory is not released either. With the Tilemaps attached, the memory consumption keeps increasing steadily, I let it continue to around 6 Gb, which it reached in a couple of minutes.
With HTML5 on Linux (‘openfl test html5’) the empty Tilemaps increase (the browser’s) CPU usage to 15% and memory usage increases at about 10 Mb/sec. With CPP the effect is a lot smaller, tested with 10000 empty objects (‘openfl test cpp -args 10000’) the empty Tilemaps cause CPU to go from 0% to 6% with memory usage increasing at 10 Kb/sec.
The issue appears similar on Windows, on Neko the empty Tilemaps push CPU usage to 18% and memory usage increases at a rate of aroung 5 Mb per second. Using HTML5 on Windows the empty Tilemaps increase CPU usage to 7% with memory usage increasing at 5 Mb per second.
The test program is very lightweight, so I have used at least 1000 empty Tilemaps to highlight the issue. In any case, using a corresponding amount of other empty DisplayObjects (Bitmaps in this case) does not increase CPU or memory usage, so whatever is happening, it appears to be related to Tilemaps. I assume that there shouldn’t be any additional CPU overhead for empty Tilemaps as opposed to other DisplayObjects (and in any case the memory usage should stay constant).
Both of the computers I tested on have an Intel Core i3-3220 CPU and an MSI GeForce GT 610 GPU.
My Linux machine has the following setup:
OS: Ubuntu 16.04 LTS (64-bit)
Haxe Compiler 3.4.7
NekoVM 2.2.0
openfl: 8.0.2
lime: 6.3.1
My Windows machine has the following setup:
OS: Windows 7 Professional SP1 (32-bit)
Haxe Compiler 3.4.7
NekoVM 2.1.0
openfl: 8.0.2
lime: 6.3.1
The test program (replaces Main.hx in the Bunnymark sample):
package;
import openfl.display.FPS;
import openfl.display.Sprite;
import openfl.display.Bitmap;
import openfl.display.Tilemap;
import openfl.events.MouseEvent;
import openfl.text.TextField;
class Main extends Sprite
{
private var fps:FPS;
private var tilemapContainer:Sprite;
private var bitmapContainer:Sprite;
private var fieldTilemaps:TextField;
private var fieldBitmaps:TextField;
public function new ()
{
super ();
// Define the number of objects
var numObjects:Int = 1000;
var args = Sys.args();
if (args.length > 0 && Std.parseInt(args[0]) != null)
numObjects = Std.parseInt(args[0]);
trace("Using " + numObjects + " objects.");
// Initialize the empty tilemaps
tilemapContainer = new Sprite();
for (i in 0...numObjects)
{
var tilemap = new Tilemap (stage.stageWidth, stage.stageHeight, null);
tilemapContainer.addChild (tilemap);
}
// Initialize the empty bitmaps
bitmapContainer = new Sprite();
for (i in 0...numObjects)
{
var bitmap = new Bitmap();
bitmapContainer.addChild (bitmap);
}
// Add a textfield "button" for attaching/detaching the tilemaps
fieldTilemaps = new TextField();
fieldTilemaps.selectable = false;
fieldTilemaps.x = 100; fieldTilemaps.y = 200;
fieldTilemaps.background = true; fieldTilemaps.backgroundColor = 0xC0C0C0;
addChild(fieldTilemaps);
fieldTilemaps.addEventListener (MouseEvent.MOUSE_UP, toggleTiles);
// Add a textfield "button" for attaching/detaching the bitmaps
fieldBitmaps = new TextField();
fieldBitmaps.selectable = false;
fieldBitmaps.x = 400; fieldBitmaps.y = 200;
fieldBitmaps.background = true; fieldBitmaps.backgroundColor = 0xC0C0C0;
addChild(fieldBitmaps);
fieldBitmaps.addEventListener (MouseEvent.MOUSE_UP, toggleBitmaps);
// Update the initial state
updateTexts();
// Add the FPS counter
fps = new FPS ();
addChild (fps);
}
private function updateTexts()
{
fieldTilemaps.text = tilemapContainer.parent != null ? "Tilemaps\nattached" : "Tilemaps\ndetached";
fieldBitmaps.text = bitmapContainer.parent != null ? "Bitmaps\nattached" : "Bitmaps\ndetached";
}
// Event Handlers
private function toggleTiles(event:MouseEvent):Void
{
if (tilemapContainer.parent != null)
removeChild(tilemapContainer);
else
addChildAt(tilemapContainer, 0);
updateTexts();
}
private function toggleBitmaps(event:MouseEvent):Void
{
if (bitmapContainer.parent != null)
removeChild(bitmapContainer);
else
addChildAt(bitmapContainer, 0);
updateTexts();
}
}