我一直致力于创建一个资产类,可以在需要时生成动态TextureAtlas
对象。具体的方法是Assets.generateTextureAtlas()
,我试图尽可能地优化它,因为我经常需要重新生成纹理图集,并希望得到比我的53ms平均值更好的时间。
53毫秒目前花了我大约3帧,这可以快速加起来我需要包装在纹理图集中的更多项目以及生成它们所需的频率。因此,我的代码中所有陷阱的答案都会很棒。
The entire class code is available here in a github gist.
RectanglePacker
类仅用于将矩形尽可能紧密地组合在一起(类似于Texture Packer),并且可以找到here。
供参考,方法如下:
public static function generateTextureAtlas(folder:String):void
{
if (!_initialised) throw new Error("Assets class not initialised.");
if (_renderTextureAtlases[folder] != null)
{
(_renderTextureAtlases[folder] as TextureAtlas).dispose();
}
var i:int;
var image:Image = new Image(_blankTexture);
var itemName:String;
var itemNames:Vector.<String> = Assets.getNames(folder + "/");
var itemsTexture:RenderTexture;
var itemTexture:Texture;
var itemTextures:Vector.<Texture> = Assets.getTextures(folder + "/");
var noOfRectangles:int;
var rect:Rectangle;
var rectanglePacker:RectanglePacker = new RectanglePacker();
var texture:Texture;
noOfRectangles = itemTextures.length;
if (noOfRectangles == 0)
{
return;
}
for (i = 0; i < noOfRectangles; i++)
{
rectanglePacker.insertRectangle(Math.round(itemTextures[i].width), Math.round(itemTextures[i].height), i);
}
rectanglePacker.packRectangles();
if (rectanglePacker.rectangleCount != noOfRectangles)
{
throw new Error("Only " + rectanglePacker.rectangleCount + " out of " + noOfRectangles + " rectangles packed for folder: " + folder);
}
itemsTexture = new RenderTexture(rectanglePacker.width, rectanglePacker.height);
itemsTexture.drawBundled(function():void
{
for (i = 0; i < noOfRectangles; i++)
{
itemTexture = itemTextures[rectanglePacker.getRectangleId(i)];
rect = rectanglePacker.getRectangle(i, rect);
image.texture = itemTexture;
image.readjustSize();
image.x = rect.x + itemTexture.frame.x;
image.y = rect.y + itemTexture.frame.y;
itemsTexture.draw(image);
}
});
_renderTextureAtlases[folder] = new TextureAtlas(itemsTexture);
for (i = 0; i < noOfRectangles; i++)
{
itemName = itemNames[rectanglePacker.getRectangleId(i)];
itemTexture = itemTextures[rectanglePacker.getRectangleId(i)];
rect = rectanglePacker.getRectangle(i);
(_renderTextureAtlases[folder] as TextureAtlas).addRegion(itemName, rect, itemTexture.frame);
}
}
答案 0 :(得分:0)
好好阅读项目&amp;找到所有可以优化的东西肯定需要时间。
首先删除对循环内rectanglePacker.getRectangle(i)
的多次调用。
例如:
itemName = itemNames[rectanglePacker.getRectangleId(i)];
itemTexture = itemTextures[rectanglePacker.getRectangleId(i)];
rect = rectanglePacker.getRectangle(i);
或许,可能是:
rect = rectanglePacker.getRectangle(i);
itemName = itemNames[rect];
itemTexture = itemTextures[rect];
如果 getRectangle 确实只是'得到一个矩形'并且&amp;没有设置任何东西。
答案 1 :(得分:0)
我认为手头上存在的更大问题是,为什么在运行期间为什么要这样做呢?在这种情况下,这种情况不会花费更多时间?这是一个广泛的操作,无论你如何优化它,你可能最终会在AS3中完成约40ms或类似的操作。
这就是为什么这些操作应该在编译期间或“加载屏幕”或其他“转换”期间完成,此时帧速率并不重要,并且当你负担得起时。
或者用c ++或其他语言创建另一个系统,它实际上可以处理数字运算,为您提供最终结果。
另外,在检查性能方面,是的,整个功能需要53ms,但是,这些毫秒使用的是什么? 53ms什么都没说,只是你发现罪魁祸首的“开销分析”,你需要把它分解成更小的块来收集一些关于实际需要时间的可靠信息,在这个功能中。
我的意思是,在该函数中,你有3个for循环,几个调用其他类,强制转换,删除,创建。它不像你在做一件事,这个函数可能会产生大约500行代码和数以万计的cpu操作。并且,您不知道它在哪里使用。我猜这是rectanglePacker.packRectangles();
需要花费60%的时间,但如果没有分析,你和我们不知道要优化什么,我们根本就没有足够的数据。
如果您必须在AS3的运行期间执行此操作,我建议在几个帧期间进行此分散,并在10帧左右均匀分配工作负载。您也可以在另一个线程和工作人员的帮助下完成它。但最重要的是,这似乎是一个设计错误,因为这可能在另一个时间完成。如果没有,那么用另一种在这种操作中更好的语言。
对此进行分析的最简单方法是添加几个类似于以下的时间戳:
var timestamps:Array = [];
然后将getTimer()
推送到代码中的不同位置,然后在功能完成后将其打印出来
答案 2 :(得分:0)
正如其他人所说,性能不佳的原因不太可能是非优化的AS代码。分析器(例如Scout)的输出非常有用。但是,如果您的目的只是添加新纹理,我可以建议几个优化:
为什么每次都需要重新生成整个地图集(调用Assets.getTextures()
并创建新的渲染纹理)?为什么不向现有地图集添加新项目?创建新的RenderTexture
(因此,GPU内存中的新纹理)是非常昂贵的操作,因为它需要CPU和GPU之间的同步。另一方面,绘制到RenderTexture
完全在GPU内部进行,因此花费的时间要少得多。
如果您将每个项目放在网格上,则可以避免使用RectanglePacker
,因为所有矩形都可以具有与网格尺寸相匹配的相同尺寸。
修改强>
澄清一下,前段时间我遇到了类似的问题:我不得不定期向现有地图集添加新项目。并且这项操作的性能是完全可以接受的(在iPad3上使用1024x1024动态纹理大约8ms)。但是我使用了包含动态图集项目的RenderTexture
和相同Sprite
对象。当我需要添加一个新项目时,我只需创建具有所需纹理的新Image
(独立或从另一个静态图集),然后将其放在Sprite
容器内,然后重新绘制此容器RenderTexture
。与删除/修改项目类似。