In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Stage3D 2D Particle Test

1つのvertexバッファでの四角パーティクルの限界数(16383)を何バッファぶん描けるかテスト

16バッファぶん=16383*16≒512*512パーティクルまで

パーティクルの動きはただの壁反射

バッファ自体は共有。

pos...CPUでの座標計算
draw...バッファのアップロード+drawTriangles()+present()
ただし合計値がFPSと合わないのでdrawが正しく計れてない可能性あり

やっぱりCPUでの座標計算がネックか
Get Adobe Flash player
by 9balls 23 Oct 2011
/**
 * Copyright 9balls ( http://wonderfl.net/user/9balls )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/eTBH
 */

package {
    import com.adobe.utils.AGALMiniAssembler;
    import com.bit101.components.NumericStepper;
    import flash.display.Sprite;
    import flash.display.Stage3D;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display3D.Context3D;
    import flash.display3D.Context3DProgramType;
    import flash.display3D.Context3DRenderMode;
    import flash.display3D.Context3DTriangleFace;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.display3D.IndexBuffer3D;
    import flash.display3D.Program3D;
    import flash.display3D.VertexBuffer3D;
    import flash.events.Event;
    import flash.geom.Matrix3D;
    import flash.text.TextField;
    import flash.utils.ByteArray;
    import flash.utils.getTimer;
    import frocessing.color.ColorHSV;

    /**
     * ...
     * @author
     */
    public class Main extends Sprite {
        private const NUM_PARTICLES:uint = 16383;
        private const NUM_BUFFERS_MAX:uint = 16;
        private const WIDTH:uint = 466;
        private const HEIGHT:uint = 466;
        private const SIZE:Number = 0.5;
        private const RAD:Number = Math.PI / 180;
        //
        private var stage3D:Stage3D;
        private var context3D:Context3D;
        //
        private var cpuPos:Vector.<Vector.<Number>>;
        private var cpuVel:Vector.<Vector.<Number>>;
        //Particle
        private var programP:Program3D;
        private var vBufferXY:VertexBuffer3D;
        private var vBufferRGB:VertexBuffer3D;
        private var vBufferOffset:VertexBuffer3D;
        private var iBuffer:IndexBuffer3D;
        //
        private var numBuffers:uint = 1;
        private var start:uint;
        private var tfR:TextField;
        private var tfP:TextField;

        public function Main():void {
            Wonderfl.disable_capture();
            stage.frameRate = 60;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            //set ui
            addChild(new GPUStats());
            var tfB:TextField = new TextField();
            tfB.textColor = 0xffffff;
            tfB.text = "Buffers : ";
            tfB.x = 100;
            addChild(tfB);
            var stepper:NumericStepper = new NumericStepper(this, 150, 0, onStepper);
            stepper.minimum = 1;
            stepper.maximum = 16;
            stepper.width = 50;
            tfP = new TextField();
            tfP.textColor = 0xffffff;
            tfP.text = "Particle : " + NUM_PARTICLES;
            tfP.x = 250;
            addChild(tfP);
            tfR = new TextField();
            tfR.textColor = 0xffffff;
            tfR.x = 400;
            addChild(tfR);
            //create input data
            var count:uint = 0;
            cpuPos = new Vector.<Vector.<Number>>(NUM_BUFFERS_MAX);
            cpuVel = new Vector.<Vector.<Number>>(NUM_BUFFERS_MAX);
            for (var j:int = 0; j < NUM_BUFFERS_MAX; j++){
                cpuPos[j] = new Vector.<Number>(NUM_PARTICLES * 8);
                cpuVel[j] = new Vector.<Number>(NUM_PARTICLES * 2);
                var index:uint = 0;
                for (var i:int = 0; i < NUM_PARTICLES; i++){
                    var posX:Number = WIDTH * Math.random();
                    var posY:Number = HEIGHT * Math.random();
                    cpuPos[j][index++] = posX;
                    cpuPos[j][index++] = posY;
                    cpuPos[j][index++] = posX;
                    cpuPos[j][index++] = posY;
                    cpuPos[j][index++] = posX;
                    cpuPos[j][index++] = posY;
                    cpuPos[j][index++] = posX;
                    cpuPos[j][index++] = posY;
                }
                index = 0;
                for (i = 0; i < NUM_PARTICLES; i++){
                    var theta:Number = 2 * Math.PI * Math.random();
                    cpuVel[j][index++] = 2 * Math.cos(theta);
                    cpuVel[j][index++] = 2 * Math.sin(theta);
                }
            }
            //
            stage3D = stage.stage3Ds[0];
            stage3D.x = 0;
            stage3D.y = 0;
            stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
            stage3D.requestContext3D(Context3DRenderMode.AUTO);
        }

        private function onContextCreate(e:Event):void {
            context3D = stage3D.context3D;
            //context3D.enableErrorChecking = true;
            //
            createShaders();
            setConstant();
            setBuffer();
            //
            context3D.configureBackBuffer(WIDTH, HEIGHT, 0, false);
            context3D.setCulling(Context3DTriangleFace.BACK);
            context3D.setVertexBufferAt(0, vBufferXY, 0, Context3DVertexBufferFormat.FLOAT_2);
            context3D.setVertexBufferAt(1, vBufferRGB, 0, Context3DVertexBufferFormat.FLOAT_3);
            context3D.setVertexBufferAt(2, vBufferOffset, 0, Context3DVertexBufferFormat.FLOAT_2);
            context3D.setProgram(programP);
            //
            stage.addEventListener(Event.ENTER_FRAME, onExe);
        }

        private function onStepper(e:Event):void {
            numBuffers = (e.currentTarget as NumericStepper).value;
            tfP.text = "Particle : " + NUM_PARTICLES * numBuffers;
        }

        private function onExe(e:Event):void {
            start = getTimer();
            calcCPU();
            var str:String = "pos : " + (getTimer() - start) + " ms";
            //
            start = getTimer();
            context3D.clear(0, 0, 0, 1);
            for (var i:int = 0; i < numBuffers; i++){
                vBufferXY.uploadFromVector(cpuPos[i], 0, NUM_PARTICLES * 4);
                context3D.drawTriangles(iBuffer);
                GPUStats.setTRI(NUM_PARTICLES * 2);
            }
            context3D.present();
            str += "\ndraw : " + (getTimer() - start) + " ms";
            tfR.text = str;
        }

        private function calcCPU():void {
            //calculate each position
            var tmp:Number;
            var pos:Vector.<Number>;
            var vel:Vector.<Number>;
            for (var j:int = 0; j < numBuffers; j++){
                var index:uint = 0;
                var index2:uint = 0;
                pos = cpuPos[j];
                vel = cpuVel[j];
                for (var i:int = 0; i < NUM_PARTICLES; i++){
                    //x
                    tmp = pos[index] + vel[index2];
                    if (tmp >= WIDTH){
                        pos[index] = WIDTH;
                        pos[index + 2] = WIDTH;
                        pos[index + 4] = WIDTH;
                        pos[index + 6] = WIDTH;
                        vel[index2] *= -1;
                    } else if (tmp <= 0){
                        pos[index] = 0;
                        pos[index + 2] = 0;
                        pos[index + 4] = 0;
                        pos[index + 6] = 0;
                        vel[index2] *= -1;
                    } else {
                        pos[index] = tmp;
                        pos[index + 2] = tmp;
                        pos[index + 4] = tmp;
                        pos[index + 6] = tmp;
                    }
                    //y
                    tmp = pos[index + 1] + vel[index2 + 1];
                    if (tmp >= HEIGHT){
                        pos[index + 1] = HEIGHT;
                        pos[index + 3] = HEIGHT;
                        pos[index + 5] = HEIGHT;
                        pos[index + 7] = HEIGHT;
                        vel[index2 + 1] *= -1;
                    } else if (tmp <= 0){
                        pos[index + 1] = 0;
                        pos[index + 3] = 0;
                        pos[index + 5] = 0;
                        pos[index + 7] = 0;
                        vel[index2 + 1] *= -1;
                    } else {
                        pos[index + 1] = tmp;
                        pos[index + 3] = tmp;
                        pos[index + 5] = tmp;
                        pos[index + 7] = tmp;
                    }
                    index += 8;
                    index2 += 2;
                }
            }
        }

        private function createShaders():void {
            var agalAssembler:AGALMiniAssembler = new AGALMiniAssembler();
            //Particle
            var code:String = "";
            code += "add vt0, va0, va2\n";
            code += "m44 vt0, vt0, vc0\n";
            code += "mov op, vt0\n";
            code += "mov v0, va1\n";
            var vertexShader:ByteArray = agalAssembler.assemble(Context3DProgramType.VERTEX, code);
            var fragmentShader:ByteArray = agalAssembler.assemble(Context3DProgramType.FRAGMENT, "mov oc, v0\n");
            programP = context3D.createProgram();
            programP.upload(vertexShader, fragmentShader);
            GPUStats.vram += (vertexShader.length + fragmentShader.length) * 4;
        }

        private function setConstant():void {
            var mtxP:Matrix3D = new Matrix3D();
            mtxP.appendScale(4 / WIDTH, -4 / HEIGHT, 1);
            mtxP.appendTranslation(-1, 1, 0);
            context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mtxP, true);
        }

        private function setBuffer():void {
            //Particle
            var numVertices:uint = NUM_PARTICLES * 4;
            var numIndices:uint = NUM_PARTICLES * 6;
            vBufferXY = context3D.createVertexBuffer(numVertices, 2);
            GPUStats.vram += numVertices * 2 * 4;
            //
            vBufferRGB = context3D.createVertexBuffer(numVertices, 3);
            GPUStats.vram += numVertices * 3 * 4;
            var cVec:Vector.<Number> = new Vector.<Number>(numVertices * 3);
            var color:ColorHSV = new ColorHSV();
            var r:Number;
            var g:Number;
            var b:Number;
            var index:uint = 0;
            for (var i:int = 0; i < NUM_PARTICLES; i++){
                color.value = 0x1000000 * Math.random();
                r = color.r / 255.0;
                g = color.g / 255.0;
                b = color.b / 255.0;
                cVec[index++] = r;
                cVec[index++] = g;
                cVec[index++] = b;
                cVec[index++] = r;
                cVec[index++] = g;
                cVec[index++] = b;
                cVec[index++] = r;
                cVec[index++] = g;
                cVec[index++] = b;
                cVec[index++] = r;
                cVec[index++] = g;
                cVec[index++] = b;
            }
            vBufferRGB.uploadFromVector(cVec, 0, numVertices);
            //
            vBufferOffset = context3D.createVertexBuffer(numVertices, 2);
            GPUStats.vram += numVertices * 2 * 4;
            var offsetVec:Vector.<Number> = new Vector.<Number>(numVertices * 2);
            index = 0;
            for (i = 0; i < NUM_PARTICLES; i++){
                offsetVec[index++] = -SIZE;
                offsetVec[index++] = -SIZE;
                offsetVec[index++] = -SIZE;
                offsetVec[index++] = SIZE;
                offsetVec[index++] = SIZE;
                offsetVec[index++] = -SIZE;
                offsetVec[index++] = SIZE;
                offsetVec[index++] = SIZE;
            }
            vBufferOffset.uploadFromVector(offsetVec, 0, numVertices);
            //
            iBuffer = context3D.createIndexBuffer(numIndices);
            GPUStats.vram += numIndices  * 4;
            var iVec:Vector.<uint> = new Vector.<uint>(numIndices);
            index = 0;
            var p:uint;
            for (i = 0; i < NUM_PARTICLES; i++){
                p = i << 2;
                iVec[index++] = p;
                iVec[index++] = p + 2;
                iVec[index++] = p + 1;
                iVec[index++] = p + 1;
                iVec[index++] = p + 2;
                iVec[index++] = p + 3;
            }
            iBuffer.uploadFromVector(iVec, 0, numIndices);
        }

    }
}

//////////////////////////////////////////////////
// GPUStats
//////////////////////////////////////////////////
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.system.System;
import flash.text.StyleSheet;
import flash.text.TextField;
import flash.utils.getTimer;

class GPUStats extends Sprite {
    protected const WIDTH:uint = 70;
    protected const HEIGHT:uint = 80;
    //
    protected var xml:XML;
    protected var text:TextField;
    protected var style:StyleSheet;
    //
    protected var timer:uint;
    protected var fps:uint;
    protected var ms:uint;
    protected var ms_prev:uint;
    protected var mem:Number;
    protected var mem_max:Number = 0;
    //
    public static var vram:uint = 0;
    public static var drw:uint = 0;
    public static var tri:uint = 0;
    //
    protected var theme:Object = {bg: 0x000033, fps: 0xffff00, ms: 0x00ff00, mem: 0x00ffff, memmax: 0xff0070, vram: 0xffffff, drw: 0xffffff, tri: 0xffffff};

    public function GPUStats(theme:Object = null):void {
        if (!theme)
            theme = this.theme;
        //
        xml = <xml><fps></fps><ms></ms><mem></mem><memMax></memMax><vram></vram><drw></drw><tri></tri></xml>;
        style = new StyleSheet();
        style.setStyle('xml', {fontSize: '9px', fontFamily: '_sans', leading: '-2px'});
        for (var name:String in theme){
            style.setStyle(name, {color: "#" + theme[name].toString(16)});
        }
        //
        text = new TextField();
        text.width = WIDTH;
        text.height = HEIGHT;
        text.styleSheet = style;
        text.condenseWhite = true;
        text.selectable = false;
        text.mouseEnabled = false;
        //
        addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
        addEventListener(Event.REMOVED_FROM_STAGE, destroy, false, 0, true);
    }

    protected function init(e:Event):void {
        graphics.beginFill(theme.bg);
        graphics.drawRect(0, 0, WIDTH, HEIGHT);
        graphics.endFill();
        addChild(text);
        addEventListener(MouseEvent.CLICK, onClick);
        addEventListener(Event.ENTER_FRAME, update);
    }

    protected function destroy(e:Event):void {
        graphics.clear();
        removeChildren();
        removeEventListener(MouseEvent.CLICK, onClick);
        removeEventListener(Event.ENTER_FRAME, update);
    }

    protected function update(e:Event):void {
        timer = getTimer();
        if (timer - 1000 > ms_prev){
            ms_prev = timer;
            mem = Number((System.totalMemory * 0.000000954).toFixed(3));
            mem_max = mem_max > mem ? mem_max : mem;
            //
            xml.fps = "FPS: " + fps + " / " + stage.frameRate;
            xml.mem = "MEM: " + mem;
            xml.memMax = "MAX: " + mem_max;
            xml.vram = "VRAM: " + (vram * 0.000000954).toFixed(3)
            xml.drw = "DRW: " + drw;
            xml.tri = "TRI: " + tri;
            //
            fps = 0;
        }
        fps++;
        xml.ms = "MS: " + (timer - ms);
        ms = timer;
        text.htmlText = xml;
        drw = 0;
        tri = 0;
    }

    protected function onClick(e:MouseEvent):void {
        mouseY / height > .5 ? stage.frameRate-- : stage.frameRate++;
        xml.fps = "FPS: " + fps + " / " + stage.frameRate;
        text.htmlText = xml;
    }

    public static function setTRI(numTriangles:uint, drwCount:uint = 1):void {
        tri += numTriangles;
        drw += drwCount;
    }
}