Question about the proper usage of Float32Array

I’m currently working on a Wolfenstein 3D-like first person shooter (2.5D, if you will) using OpenFL’s OpenGL implementation for desktops, browsers, and mobile.

My question is that, will my constant creation of a new Float32Array (more than once per frame) to pass data severely decrease performance in the long run? I find that I have to constantly use the Float32Array constructor, since there doesn’t seem to be a way to “reuse” a previous Float32Array.

Actually you can reuse Float32Array, here is example:

package ;

import haxe.Timer;
import lime.math.Matrix4;
import openfl.display.OpenGLView;
import openfl.display.Sprite;
import openfl.geom.Rectangle;
import openfl.gl.GL;
import openfl.gl.GLBuffer;
import openfl.gl.GLProgram;
import openfl.utils.Float32Array;

class Main extends Sprite {
    private var shaderProgram:GLProgram;
    private var vertexAttribute:Int;
    private var vertexBuffer:GLBuffer;
    private var view:OpenGLView;
    private var vertices:Float32Array;
    private var startTime:Float = -1.0;
    private var indices:Int = 0;

    public function new() {
        super();

        if (!OpenGLView.isSupported) {
            return;
        }

        view = new OpenGLView();
        createProgram();

        vertexBuffer = GL.createBuffer();
        vertices = new Float32Array(800 * 4 * 3);

        view.render = renderView;
        addChild(view);
    }

    private function createProgram():Void {
        var vertexShaderSource = "
            attribute vec3 vertexPosition;
            uniform mat4 modelViewMatrix;
            uniform mat4 projectionMatrix;

            void main(void) {
                gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1.0);
            }
        ";

        var vertexShader = GL.createShader(GL.VERTEX_SHADER);
        GL.shaderSource(vertexShader, vertexShaderSource);
        GL.compileShader(vertexShader);

        if (GL.getShaderParameter (vertexShader, GL.COMPILE_STATUS) == 0) {
            throw "Error compiling vertex shader";
        }

        var fragmentShaderSource = "
            void main(void) {
                gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
            }
        ";

        var fragmentShader = GL.createShader(GL.FRAGMENT_SHADER);
        GL.shaderSource(fragmentShader, fragmentShaderSource);
        GL.compileShader(fragmentShader);

        if (GL.getShaderParameter(fragmentShader, GL.COMPILE_STATUS) == 0) {
            throw "Error compiling fragment shader";
        }

        shaderProgram = GL.createProgram();
        GL.attachShader(shaderProgram, vertexShader);
        GL.attachShader(shaderProgram, fragmentShader);
        GL.linkProgram(shaderProgram);

        if (GL.getProgramParameter(shaderProgram, GL.LINK_STATUS) == 0) {
            throw "Unable to initialize the shader program.";
        }

        vertexAttribute = GL.getAttribLocation(shaderProgram, "vertexPosition");
    }

    private function update():Void {
        var currentTime = Timer.stamp();

        if (startTime < 0) {
            startTime = currentTime;
        }

        indices = 0;
        var pos:Int = 0;
        var dt:Int = Std.int((currentTime - startTime) * 1000.0);

        for (i in -400 ... 400) {
            var hgt:Float = Math.sin(dt / 200 + i / 400) * 110 + 120;

            vertices[pos++] = i + 1;
            vertices[pos++] = hgt;
            vertices[pos++] = 0;

            vertices[pos++] = i;
            vertices[pos++] = hgt;
            vertices[pos++] = 0;

            vertices[pos++] = i + 1;
            vertices[pos++] = -hgt;
            vertices[pos++] = 0;

            vertices[pos++] = i;
            vertices[pos++] = -hgt;
            vertices[pos++] = 0;

            indices += 4;
        }

        GL.bufferData(GL.ARRAY_BUFFER, vertices, GL.DYNAMIC_DRAW);
    }

    private function renderView(rect:Rectangle):Void {
        GL.viewport(Std.int(rect.x), Std.int(rect.y), Std.int(rect.width), Std.int(rect.height));

        GL.clearColor(8.0 / 256.0, 146.0 / 256.9, 208.0 / 256.0, 1);
        GL.clear(GL.COLOR_BUFFER_BIT);

        var positionX = rect.width / 2;
        var positionY = rect.height / 2;

        var projectionMatrix = Matrix4.createOrtho(0, rect.width, rect.height, 0, 1000, -1000);
        var modelViewMatrix = Matrix4.create2D(positionX, positionY, 1, 0);

        GL.useProgram(shaderProgram);
        GL.enableVertexAttribArray(vertexAttribute);

        GL.bindBuffer(GL.ARRAY_BUFFER, vertexBuffer);
        update();
        GL.vertexAttribPointer(vertexAttribute, 3, GL.FLOAT, false, 0, 0);

        var projectionMatrixUniform = GL.getUniformLocation(shaderProgram, "projectionMatrix");
        var modelViewMatrixUniform = GL.getUniformLocation(shaderProgram, "modelViewMatrix");

        GL.uniformMatrix4fv(projectionMatrixUniform, false, projectionMatrix);
        GL.uniformMatrix4fv(modelViewMatrixUniform, false, modelViewMatrix);

        GL.drawArrays(GL.TRIANGLE_STRIP, 0, indices);
        GL.bindBuffer(GL.ARRAY_BUFFER, null);
    }
}

By the way, FPS in 2.5D it is interesting :slight_smile:

Thank you! That works well. I forgot that Float32Array has array access implemented.

It’s possible to use this library to manipulate Float32Arrays. It doesn’t allow you to do anything you couldn’t do with array access, it just helps you keep track of what’s going on.

(You’ll have to add <haxedef name="vertexData_float32Array" /> to project.xml in order to make it work. Not sure if I should make this the default and Vector<Float> the optional one.)