12

これは、さまざまなビルド (Windows c++、Flash) の NME のピクセル レベルの操作パフォーマンスをテストするための小さなプロジェクトです。

ピクセルを 1 つずつ変更するために使用BitmapData.setPixelします (フレームごとに 320x240)。C++ ビルドは 22 FPS で実行され、フラッシュビルドは約 100 FPSで実行されます。フラッシュと比較して C++ ビルドのパフォーマンスが大幅に低下する理由は何ですか? C++ ビルドを使用してより高い FPS を取得するようにコードを改善するにはどうすればよいですか?

マンデルブロ.hx

import nme.display.Sprite;
import nme.display.Bitmap;
import nme.display.BitmapData;
import nme.text.TextField;
import nme.events.Event;
import nme.events.TimerEvent;
import nme.utils.Timer;
import nme.geom.Matrix;
import nme.geom.Rectangle;
import nme.utils.ByteArray;

class Mandelbrot
{
    public static function main() : Void
    {
        new Mandelbrot();
    }

    public var pixels:Array<Array<Int>>;

    public var colorModifier:Int;
    private var bitmapData:BitmapData;
    private var bigBitmapData:BitmapData;

    private var fps:TextField;

    private var width:Int;
    private var height:Int;
    private var matrix:Matrix;

    public function new() 
    {
        width = 320; //Std.int(flash.Lib.current.stage.stageWidth/2);
        height = 240; //Std.int(flash.Lib.current.stage.stageHeight/2);

        var scale:Float = 2;//flash.Lib.current.stage.stageWidth/width;
        matrix = new Matrix();
        matrix.scale(scale, scale);

        var setBitmap:Bitmap = new Bitmap();
        bitmapData = new BitmapData( width , height , false , 0x000000 );
        bigBitmapData = new BitmapData( nme.Lib.current.stage.stageWidth , nme.Lib.current.stage.stageHeight , false , 0x000000 );

        setBitmap.bitmapData = bigBitmapData;

        nme.Lib.current.addChild( setBitmap );

        var maxIterations:Int = 128;

        pixels = new Array();


        var beforeTime = nme.Lib.getTimer();

        var xtemp;
        var iteration;
        var x0:Float = 0;
        var y0:Float = 0;
        for(ix in 0...width) {
            pixels[ix] = new Array();
            for(iy in 0...height) {
                    x0 = 0;
                    y0 = 0;
                    iteration = 128;

                    while ( x0*x0 + y0*y0 <= 4  &&  iteration > 0 ) 
                    {
                        xtemp = x0*x0 - y0*y0 + (ix-14*5000)/50000;
                        y0 = 2*x0*y0 + (iy-(height/0.6))/50000;
                        x0 = xtemp;

                        iteration--;
                    }

                    pixels[ix][iy] = iteration;
            }
        }

        var afterTime = nme.Lib.getTimer();

        var tf = new TextField();
        tf.width = 400;
        tf.text = "Generating fractal took "+(afterTime-beforeTime)+" ms";
        nme.Lib.current.addChild(tf);

        fps = new TextField();
        fps.width = 400;
        fps.y = 10;
        fps.text = "FPS: ";
        nme.Lib.current.addChild(fps);

        colorModifier = 2;
        var timer:haxe.Timer = new haxe.Timer(10);

        runLoop();
        timer.run = runLoop;
    }

    public function runLoop() {
        var r:Int=0, b:Int=0, g:Int=0;
        var pixel:Int = 0;

        var beforeTime = nme.Lib.getTimer();

        for(iy in 0...height) {
            for(ix in 0...width) {
                pixel = pixels[ix][iy];
                r = pixel + colorModifier;
                g = pixel + colorModifier + r;
                b = pixel + colorModifier + g;
                bitmapData.setPixel(ix, iy, (r<<16 | g<<8 | b));
            }
        }

        bigBitmapData.draw(bitmapData, matrix, null, null, null, false);
        var afterTime = nme.Lib.getTimer();
        fps.text = "FPS: "+Math.round(1000/(afterTime-beforeTime));

        colorModifier += 2;
        if(colorModifier > 65530)
                colorModifier = 0;


    }
}

マンデルブロ.nml

<?xml version="1.0" encoding="utf-8"?>
<project>
  <app
     file="Mandelbrot.hx"
     title="Mandelbrot sample"
     package="org.haxe.nme.mandelbrot"
     version="1.0.0"
     company="nme" 
     main="Mandelbrot"
  />
  <window
        width="640"
        height="480"
        orientation="landscape"
        fps="60"
        background="0xffffff"
        resizeable="true"
        hardware="true"
    />
  <classpath name="." />
  <haxelib name="nme" />
  <ndll name="std" />
  <ndll name="regexp" />
  <ndll name="zlib" />
  <ndll name="nme" haxelib="nme" />
  <setenv name="SHOW_CONSOLE"/>
</project>
4

3 に答える 3

14

nme.MemoryAPIを調べてください。アイデアはByteArray、正しいサイズで を作成し (または から取得しBitmapData)、それを現在の仮想メモリ空​​間として選択し、そのバイトを直接操作することです。

Flash では約 10 倍の速度ブーストが得られ、CPP ターゲットでもより高速になるはずです。リリース モードでコンパイルすることを忘れないでください。そうしないと、メソッドのインライン化が無効になり、パフォーマンスが大幅に低下します。

基本的な使用例 (テストされていないコード):

var rect:Rectangle = bitmapData.rect;

// 32bits integer = 4 bytes
var size:Int = bitmapData.width * bitmapData.height * 4;

// The virtual memory space we'll use
var pixels:ByteArray = new ByteArray();

// CPP does not support setting the length property directly
#if (cpp) pixels.setLength(size);
#else pixels.length = size; #end

// Select the memory space (call it once, not every frame)
Memory.select(pixels);

// And in your loop set your color
// Color is in BGRA mode, nme.Memory can only be used in little endian mode.
Memory.setI32((y * width + x) * 4, color);

// When you're done, render the BitmapData
// (don't forget to reset the ByteArray position)
pixels.position = 0;
bitmapData.setPixels(rect, pixels);

これは非常に基本的なコード例であることに注意してください。あなたの場合、ByteArray反復回数も保存する必要があるため、それを適応させて実際に2倍のサイズを使用する必要があります。ネストされたループはメイン ループで最適化でき、多くの余分なインデックス/アドレス計算を回避できます。

// Note the size * 2 !
// First part of the ByteArray will be used to store the iteration count,
// the second part to draw the pixels.
#if (cpp) pixels.setLength(size * 2);
#else pixels.length = size * 2; #end

Memory.select(pixels);

// First loop storing iteration count
for (iy in 0...height)
{
    for (ix in 0...width)
    {
        // ... do some stuff ...
        Memory.setI32((iy * width + ix) << 2, iteration);
    }
}

// In your runLoop :
for (i in 0...(height * width))
{
    // Get the iteration count
    var pixel:Int = Memory.getI32(i << 2);

    r = pixel + colorModifier;
    g = pixel + colorModifier + r;
    b = pixel + colorModifier + g;

    // Note that we're writing the pixel in the second part of our ByteArray
    Memory.setI32(size + (i << 2), r | g << 8 | b << 16);
}

// Sets the position to the second part of our ByteArray
pixels.position = size;
bitmapData.setPixels(rect, pixels);

で、これです。Flash ターゲットで Alchemy オペコードを本当に使用したくない場合は、ピクセルをブリットする次の最速の方法は、クラスからgetVector()/を使用することです。しかし、実際にはそれほど速くはありません。setVector()BitmapData

于 2012-04-25T20:43:49.157 に答える
1

配列自体は、フラッシュの真の線形配列ではなく、マップに似ています。ピクセルごとの操作については、BitmapData クラスの getVector/setVector API を使用することをお勧めします。この API は、画像の四角形の領域をフラット ピクセル データとして取得 (および割り当て) できます。その場合、ベクトル内の個々のピクセルに次のようにアクセスできます。

pixels[ix + image_width*iy] = <argb32>

また、配列の中間配列を構築する代わりに、ピクセルを直接割り当てる方が高速です。

于 2012-04-25T16:57:24.463 に答える
0

ByteArrayを使用してみてください。FlashとC++の方が速いと思います。

于 2012-04-25T14:11:50.287 に答える