Selaa lähdekoodia

Better engine buffer support

Gary Hsu 7 vuotta sitten
vanhempi
commit
eefb85c7ac

+ 1 - 1
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -1357,7 +1357,7 @@ module BABYLON.GLTF2 {
                 accessor._babylonVertexBuffer = this._loadVertexBufferViewAsync(bufferView, kind).then(buffer => {
                     const size = GLTFLoader._GetNumComponents(context, accessor.type);
                     return new VertexBuffer(this.babylonScene.getEngine(), buffer, kind, false, false, bufferView.byteStride,
-                        false, accessor.byteOffset, size, accessor.componentType, accessor.normalized, true);
+                        false, accessor.byteOffset, size, accessor.componentType, accessor.normalized, true, accessor.count);
                 });
             }
 

+ 26 - 28
src/Engine/babylon.engine.ts

@@ -2637,40 +2637,38 @@
 
             this.bindIndexBuffer(vbo);
 
-            // Check for 32 bits indices
-            var arrayBuffer;
-            var need32Bits = false;
+            const data = this._normalizeIndexData(indices);
+            this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER, data, updatable ? this._gl.DYNAMIC_DRAW : this._gl.STATIC_DRAW);
+            this._resetIndexBufferBinding();
+            vbo.references = 1;
+            vbo.is32Bits = (data.BYTES_PER_ELEMENT === 4);
+            return vbo;
+        }
 
+        protected _normalizeIndexData(indices: IndicesArray): Uint16Array | Uint32Array
+        {
             if (indices instanceof Uint16Array) {
-                arrayBuffer = indices;
-            } else {
-                //check 32 bit support
-                if (this._caps.uintIndices) {
-                    if (indices instanceof Uint32Array) {
-                        arrayBuffer = indices;
-                        need32Bits = true;
-                    } else {
-                        //number[] or Int32Array, check if 32 bit is necessary
-                        for (var index = 0; index < indices.length; index++) {
-                            if (indices[index] > 65535) {
-                                need32Bits = true;
-                                break;
-                            }
-                        }
+                return indices;
+            }
 
-                        arrayBuffer = need32Bits ? new Uint32Array(indices) : new Uint16Array(indices);
-                    }
+            // Check 32 bit support
+            if (this._caps.uintIndices) {
+                if (indices instanceof Uint32Array) {
+                    return indices;
                 } else {
-                    //no 32 bit support, force conversion to 16 bit (values greater 16 bit are lost)
-                    arrayBuffer = new Uint16Array(indices);
+                    // number[] or Int32Array, check if 32 bit is necessary
+                    for (var index = 0; index < indices.length; index++) {
+                        if (indices[index] > 65535) {
+                            return new Uint32Array(indices);
+                        }
+                    }
+
+                    return new Uint16Array(indices);
                 }
             }
 
-            this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER, arrayBuffer, updatable ? this._gl.DYNAMIC_DRAW : this._gl.STATIC_DRAW);
-            this._resetIndexBufferBinding();
-            vbo.references = 1;
-            vbo.is32Bits = need32Bits;
-            return vbo;
+            // No 32 bit support, force conversion to 16 bit (values greater 16 bit are lost)
+            return new Uint16Array(indices);
         }
 
         /**
@@ -2956,7 +2954,7 @@
             buffer.references--;
 
             if (buffer.references === 0) {
-                this._gl.deleteBuffer(buffer);
+                this._deleteBuffer(buffer);
                 return true;
             }
 

+ 79 - 66
src/Engine/babylon.nativeEngineWrapper.ts

@@ -3,11 +3,13 @@
     export interface INativeEngineInterop {
         requestAnimationFrame(callback: () => void): void;
 
-        createIndexBuffer(indices: DataView, is32Bits: boolean): WebGLBuffer;
-        bindIndexBuffer(buffer: WebGLBuffer): void;
-        createVertexBuffer(vertices: Float32Array): WebGLBuffer;
-        bindVertexBuffer(buffer: WebGLBuffer, index: number, stride: number, offset: number): void;
-        deleteBuffer(buffer: WebGLBuffer): void;
+        createIndexBuffer(data: ArrayBufferView): any;
+        bindIndexBuffer(buffer: any): void;
+        deleteIndexBuffer(buffer: any): void;
+
+        createVertexBuffer(data: ArrayBufferView, elementCount: number): any;
+        bindVertexBuffer(buffer: any, location: number, byteOffset: number, byteStride: number): void;
+        deleteVertexBuffer(buffer: any): void;
 
         createProgram(vertexShader: string, fragmentShader: string): WebGLProgram;
         getUniforms(shaderProgram: WebGLProgram, uniformsNames: string[]): WebGLUniformLocation[];
@@ -51,6 +53,18 @@
         getRenderHeight(): number;
     }
 
+    interface VertexBufferInfo extends VertexBuffer {
+        _nativeBuffer?: any;
+    }
+
+    interface WebGLBufferInfo extends WebGLBuffer {
+        _nativeIndexBuffer?: any;
+        _nativeVertexBuffer?: any;
+
+        // Vertex buffers that are holding native vertex buffers that required conversion to float
+        _vertexBuffers?: VertexBufferInfo[];
+    }
+
     class NativeFilter {
         // Must match Filter enum in SpectreEngine.h.
         public static readonly POINT = 0;
@@ -195,84 +209,65 @@
             this._interop.clear(color.r, color.g, color.b, color.a, backBuffer, depth, stencil);
         }
 
-        // TODO: Share more of this logic with the base Engine class implementation.
-        public createIndexBuffer(indices: IndicesArray): WebGLBuffer {
-            var dataView: DataView;
-            var is32Bits = false;
-            if (indices instanceof Uint16Array) {
-                dataView = new DataView(indices.buffer, indices.byteOffset, indices.byteLength);
-            } else if (indices instanceof Uint32Array) {
-                dataView = new DataView(indices.buffer, indices.byteOffset, indices.byteLength);
-                is32Bits = true;
-            } else {
-                //number[] or Int32Array, check if 32 bit is necessary
-                for (var index = 0; index < indices.length; index++) {
-                    if (indices[index] > 65535) {
-                        is32Bits = true;
-                        break;
-                    }
-                }
-
-                if (is32Bits) {
-                    let array = new Uint32Array(indices);
-                    dataView = new DataView(array.buffer, array.byteOffset, array.byteLength);
-                } else {
-                    let array = new Uint16Array(indices);
-                    dataView = new DataView(array.buffer, array.byteOffset, array.byteLength);
-                }
-            }
-
-            const buffer = this._interop.createIndexBuffer(dataView, is32Bits);
-            buffer.capacity = indices.length;
-            buffer.references = 1;
-            buffer.is32Bits = is32Bits;
-
-            return buffer;
+        public createIndexBuffer(indices: IndicesArray): WebGLBufferInfo {
+            const data = this._normalizeIndexData(indices);
+            return {
+                references: 1,
+                is32Bits: (data.BYTES_PER_ELEMENT === 4),
+                _nativeIndexBuffer: this._interop.createIndexBuffer(data),
+            } as WebGLBufferInfo;
         }
 
-        public createVertexBuffer(data: DataArray): WebGLBuffer {
-            let floatArray: Float32Array;
-            if (data instanceof Array) {
-                floatArray = new Float32Array(data);
-            } else if (data instanceof ArrayBuffer) {
-                floatArray = new Float32Array(data);
-            } else {
-                floatArray = new Float32Array(data.buffer, data.byteOffset, data.byteLength / 4);
-            }
-
-            const buffer = this._interop.createVertexBuffer(floatArray);
-            buffer.capacity = floatArray.length;
-            buffer.references = 1;
-            buffer.is32Bits = true;
-
-            return buffer;
+        public createVertexBuffer(data: DataArray): WebGLBufferInfo {
+            return {
+                references: 1
+            } as WebGLBufferInfo;
         }
 
-        // BUFFERS.
-        public bindBuffers(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: WebGLBuffer, effect: Effect): void {
+        public bindBuffers(vertexBuffers: { [key: string]: VertexBufferInfo }, indexBuffer: WebGLBufferInfo, effect: Effect): void {
             // Index
             if (indexBuffer) {
-                this._interop.bindIndexBuffer(indexBuffer);
+                this._interop.bindIndexBuffer(indexBuffer._nativeIndexBuffer);
             }
 
             // Vertex
             var attributes = effect.getAttributesNames();
             for (var index = 0; index < attributes.length; index++) {
-                var order = effect.getAttributeLocation(index);
-                if (order >= 0) {
+                var location = effect.getAttributeLocation(index);
+                if (location >= 0) {
                     var vertexBuffer = vertexBuffers[attributes[index]];
                     if (!vertexBuffer) {
                         continue;
                     }
 
-                    var buffer = vertexBuffer.getBuffer();
+                    var buffer = vertexBuffer.getBuffer() as Nullable<WebGLBufferInfo>;
                     if (buffer) {
-                        this._interop.bindVertexBuffer(buffer, order, vertexBuffer.byteStride, vertexBuffer.byteOffset);
+                        const data = vertexBuffer.getData();
+                        if (data) {
+                            if (vertexBuffer.type === VertexBuffer.FLOAT && ArrayBuffer.isView(data)) {
+                                buffer._nativeVertexBuffer = buffer._nativeVertexBuffer || this._interop.createVertexBuffer(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), vertexBuffer.count * vertexBuffer.getSize());
+                                this._interop.bindVertexBuffer(buffer._nativeVertexBuffer, location, vertexBuffer.byteOffset, vertexBuffer.byteStride);
+                            }
+                            else {
+                                // TODO: do this only for implementations that require it.
+                                // Convert to float as WebGL auto converts types but DirectX native implementations do not.
+                                let nativeBuffer = vertexBuffer._nativeBuffer;
+                                if (!nativeBuffer) {
+                                    const floatData = new Float32Array(vertexBuffer.count * vertexBuffer.getSize());
+                                    vertexBuffer.forEach(floatData.length, (value, index) => floatData[index] = value);
+                                    nativeBuffer = this._interop.createVertexBuffer(new Uint8Array(floatData.buffer), floatData.length);
+                                    vertexBuffer._nativeBuffer = nativeBuffer;
+                                    buffer._vertexBuffers = buffer._vertexBuffers || [];
+                                    buffer._vertexBuffers.push(vertexBuffer);
+                                }
+                                this._interop.bindVertexBuffer(nativeBuffer, location, 0, vertexBuffer.getSize() * Float32Array.BYTES_PER_ELEMENT);
+                            }
+                        }
                     }
                 }
             }
         }
-        
+
         public getAttributes(shaderProgram: WebGLProgram, attributesNames: string[]): number[] {
             return this._interop.getAttributes(shaderProgram, attributesNames);
         }
@@ -872,7 +867,7 @@
             throw new Error("unBindFramebuffer not yet implemented.")
         }
 
-        public createDynamicVertexBuffer(vertices: FloatArray): WebGLBuffer {
+        public createDynamicVertexBuffer(data: DataArray): WebGLBuffer {
             throw new Error("createDynamicVertexBuffer not yet implemented.")
         }
 
@@ -887,7 +882,7 @@
          * @param byteOffset the byte offset of the data (optional)
          * @param byteLength the byte length of the data (optional)
          */
-        public updateDynamicVertexBuffer(vertexBuffer: WebGLBuffer, vertices: FloatArray, byteOffset?: number, byteLength?: number): void {
+        public updateDynamicVertexBuffer(vertexBuffer: WebGLBuffer, data: DataArray, byteOffset?: number, byteLength?: number): void {
             throw new Error("updateDynamicVertexBuffer not yet implemented.")
         }
 
@@ -967,8 +962,26 @@
             throw new Error("_bindTexture not implemented.");
         }
 
-        protected _deleteBuffer(buffer: WebGLBuffer): void {
-            this._interop.deleteBuffer(buffer);
+        protected _deleteBuffer(buffer: WebGLBufferInfo): void {
+            if (buffer._nativeIndexBuffer) {
+                this._interop.deleteIndexBuffer(buffer._nativeIndexBuffer);
+                delete buffer._nativeIndexBuffer;
+            }
+
+            if (buffer._nativeVertexBuffer) {
+                this._interop.deleteVertexBuffer(buffer._nativeVertexBuffer);
+                delete buffer._nativeVertexBuffer;
+            }
+
+            if (buffer._vertexBuffers) {
+                for (const vertexBuffer of buffer._vertexBuffers) {
+                    const nativeBuffer = vertexBuffer._nativeBuffer;
+                    if (nativeBuffer) {
+                        this._interop.deleteVertexBuffer(nativeBuffer);
+                        delete vertexBuffer._nativeBuffer;
+                    }
+                }
+            }
         }
 
         public releaseEffects() {

+ 15 - 0
src/Mesh/babylon.buffer.ts

@@ -13,6 +13,21 @@
         public readonly byteStride: number;
 
         /**
+         * Gets the byte length.
+         */
+        public get byteLength(): number {
+            if (!this._data) {
+                return 0;
+            }
+
+            if (this._data instanceof Array) {
+                return this._data.length * Float32Array.BYTES_PER_ELEMENT;
+            }
+
+            return this._data.byteLength;
+        }
+
+        /**
          * Constructor
          * @param engine the engine
          * @param data the data to use for this buffer

+ 18 - 3
src/Mesh/babylon.vertexBuffer.ts

@@ -70,6 +70,13 @@
         public readonly byteOffset: number;
 
         /**
+         * Gets the byte length.
+         */
+        public get byteLength(): number {
+            return this._buffer.byteLength - this.byteOffset;
+        }
+
+        /**
          * Gets whether integer data values should be normalized into a certain range when being casted to a float.
          */
         public readonly normalized: boolean;
@@ -80,6 +87,11 @@
         public readonly type: number;
 
         /**
+         * Gets the number of attributes.
+         */
+        public readonly count: number;
+
+        /**
          * Constructor
          * @param engine the engine
          * @param data the data to use for this vertex buffer
@@ -89,12 +101,13 @@
          * @param stride the stride (optional)
          * @param instanced whether the buffer is instanced (optional)
          * @param offset the offset of the data (optional)
-         * @param size the number of components (optional)
+         * @param size the number of components per attribute (optional)
          * @param type the type of the component (optional)
          * @param normalized whether the data contains normalized data (optional)
          * @param useBytes set to true if stride and offset are in bytes (optional)
+         * @param count the number of attributes (optional)
          */
-        constructor(engine: any, data: DataArray | Buffer, kind: string, updatable: boolean, postponeInternalCreation?: boolean, stride?: number, instanced?: boolean, offset?: number, size?: number, type?: number, normalized = false, useBytes = false) {
+        constructor(engine: any, data: DataArray | Buffer, kind: string, updatable: boolean, postponeInternalCreation?: boolean, stride?: number, instanced?: boolean, offset?: number, size?: number, type?: number, normalized = false, useBytes = false, count = 0) {
             if (data instanceof Buffer) {
                 this._buffer = data;
                 this._ownsBuffer = false;
@@ -136,6 +149,8 @@
 
             this._instanced = instanced !== undefined ? instanced : false;
             this._instanceDivisor = instanced ? 1 : 0;
+
+            this.count = count || (this.byteLength / this.byteStride);
         }
 
         /** @hidden */
@@ -390,7 +405,7 @@
          * @param byteStride the byte stride of the data
          * @param componentCount the number of components per element
          * @param componentType the type of the component
-         * @param count the total number of components
+         * @param count the total number of components to enumerate
          * @param normalized whether the data is normalized
          * @param callback the callback function called for each value
          */