Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions src/animation/AnimationUtils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Quaternion } from '../math/Quaternion.js';
import { AdditiveAnimationBlendMode } from '../constants.js';
import { isTypedArray } from '../utils.js';

/**
* Converts an array to a specific type.
Expand All @@ -22,18 +23,6 @@ function convertArray( array, type ) {

}

/**
* Returns `true` if the given object is a typed array.
*
* @param {any} object - The object to check.
* @return {boolean} Whether the given object is a typed array.
*/
function isTypedArray( object ) {

return ArrayBuffer.isView( object ) && ! ( object instanceof DataView );

}

/**
* Returns an array by which times and values can be sorted.
*
Expand Down
28 changes: 28 additions & 0 deletions src/nodes/accessors/BufferNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,34 @@ class BufferNode extends UniformNode {
*/
this.bufferCount = bufferCount;

/**
* An array of update ranges.
*
* @type {Array<{start: number, count: number}>}
*/
this.updateRanges = [];

}

/**
* Adds a range of data in the data array to be updated on the GPU.
*
* @param {number} start - Position at which to start update.
* @param {number} count - The number of components to update.
*/
addUpdateRange( start, count ) {

this.updateRanges.push( { start, count } );

}

/**
* Clears the update ranges.
*/
clearUpdateRanges() {

this.updateRanges.length = 0;

}

/**
Expand Down
40 changes: 40 additions & 0 deletions src/renderers/common/Buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,46 @@ class Buffer extends Binding {
*/
this._buffer = buffer;

/**
* An array of update ranges.
*
* @private
* @type {Array<{start: number, count: number}>}
*/
this._updateRanges = [];

}

/**
* The array of update ranges.
*
* @type {Array<{start: number, count: number}>}
*/
get updateRanges() {

return this._updateRanges;

}

/**
* Adds an update range.
*
* @param {number} start - The start index.
* @param {number} count - The number of elements.
*/
addUpdateRange( start, count ) {

this.updateRanges.push( { start, count } );

}

/**
* Clears all update ranges.
*/
clearUpdateRanges() {

this.updateRanges.length = 0;

}

/**
Expand Down
52 changes: 52 additions & 0 deletions src/renderers/common/nodes/NodeUniformBuffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,58 @@ class NodeUniformBuffer extends UniformBuffer {
*/
this.groupNode = groupNode;

/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isNodeUniformBuffer = true;

}

/**
* The array of update ranges.
*
* @param {Array<{start: number, count: number}>} value - The update ranges.
*/
set updateRanges( value ) {

this.nodeUniform.updateRanges = value;

}

/**
* The array of update ranges.
*
* @type {Array<{start: number, count: number}>}
*/
get updateRanges() {

return this.nodeUniform.updateRanges;

}

/**
* Adds a range of data in the data array to be updated on the GPU.
*
* @param {number} start - Position at which to start update.
* @param {number} count - The number of components to update.
*/
addUpdateRange( start, count ) {

this.nodeUniform.addUpdateRange( start, count );

}

/**
* Clears all update ranges.
*/
clearUpdateRanges() {

this.nodeUniform.clearUpdateRanges();

}

/**
Expand Down
73 changes: 63 additions & 10 deletions src/renderers/webgl-fallback/WebGLBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import WebGLCapabilities from './utils/WebGLCapabilities.js';
import { GLFeatureName } from './utils/WebGLConstants.js';
import { WebGLBufferRenderer } from './WebGLBufferRenderer.js';

import { warnOnce, warn, error } from '../../utils.js';
import { isTypedArray, warnOnce, warn, error } from '../../utils.js';
import { WebGLCoordinateSystem, TimestampQuery } from '../../constants.js';
import WebGLTimestampQueryPool from './utils/WebGLTimestampQueryPool.js';

Expand Down Expand Up @@ -1743,25 +1743,53 @@ class WebGLBackend extends Backend {

if ( binding.isUniformsGroup || binding.isUniformBuffer ) {

const data = binding.buffer;
let { bufferGPU } = this.get( data );
const array = binding.buffer;
let { bufferGPU } = this.get( array );

if ( bufferGPU === undefined ) {

// create

bufferGPU = gl.createBuffer();

gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
gl.bufferData( gl.UNIFORM_BUFFER, data, gl.DYNAMIC_DRAW );
gl.bufferData( gl.UNIFORM_BUFFER, array.byteLength, gl.DYNAMIC_DRAW );

this.set( data, { bufferGPU } );
this.set( array, { bufferGPU } );

} else {

// update

gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
gl.bufferSubData( gl.UNIFORM_BUFFER, 0, data );

}

// update

const updateRanges = binding.updateRanges;

gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );

if ( updateRanges.length === 0 ) {

gl.bufferData( gl.UNIFORM_BUFFER, array, gl.DYNAMIC_DRAW );

} else {

const isTyped = isTypedArray( array );
const byteOffsetFactor = isTyped ? 1 : array.BYTES_PER_ELEMENT;

for ( let i = 0, l = updateRanges.length; i < l; i ++ ) {

const range = updateRanges[ i ];

const dataOffset = range.start * byteOffsetFactor;
const size = range.count * byteOffsetFactor;

const bufferOffset = dataOffset * ( isTyped ? array.BYTES_PER_ELEMENT : 1 ); // bufferOffset is always in bytes

gl.bufferSubData( gl.UNIFORM_BUFFER, bufferOffset, array, dataOffset, size );

}

}

Expand Down Expand Up @@ -1799,10 +1827,35 @@ class WebGLBackend extends Backend {

const bindingData = this.get( binding );
const bufferGPU = bindingData.bufferGPU;
const data = binding.buffer;
const array = binding.buffer;

const updateRanges = binding.updateRanges;

gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
gl.bufferData( gl.UNIFORM_BUFFER, data, gl.DYNAMIC_DRAW );

if ( updateRanges.length === 0 ) {

gl.bufferData( gl.UNIFORM_BUFFER, array, gl.DYNAMIC_DRAW );

} else {

const isTyped = isTypedArray( array );
const byteOffsetFactor = isTyped ? 1 : array.BYTES_PER_ELEMENT;

for ( let i = 0, l = updateRanges.length; i < l; i ++ ) {

const range = updateRanges[ i ];

const dataOffset = range.start * byteOffsetFactor;
const size = range.count * byteOffsetFactor;

const bufferOffset = dataOffset * ( isTyped ? array.BYTES_PER_ELEMENT : 1 ); // bufferOffset is always in bytes

gl.bufferSubData( gl.UNIFORM_BUFFER, bufferOffset, array, dataOffset, size );

}

}

}

Expand Down
21 changes: 4 additions & 17 deletions src/renderers/webgpu/utils/WebGPUAttributeUtils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GPUInputStepMode } from './WebGPUConstants.js';

import { Float16BufferAttribute } from '../../../core/BufferAttribute.js';
import { error } from '../../../utils.js';
import { isTypedArray, error } from '../../../utils.js';

const typedArraysToVertexFormatPrefix = new Map( [
[ Int8Array, [ 'sint8', 'snorm8' ]],
Expand Down Expand Up @@ -174,7 +174,6 @@ class WebGPUAttributeUtils {
}


const isTypedArray = this._isTypedArray( array );
const updateRanges = bufferAttribute.updateRanges;

if ( updateRanges.length === 0 ) {
Expand All @@ -190,7 +189,8 @@ class WebGPUAttributeUtils {

} else {

const byteOffsetFactor = isTypedArray ? 1 : array.BYTES_PER_ELEMENT;
const isTyped = isTypedArray( array );
const byteOffsetFactor = isTyped ? 1 : array.BYTES_PER_ELEMENT;

for ( let i = 0, l = updateRanges.length; i < l; i ++ ) {

Expand All @@ -211,7 +211,7 @@ class WebGPUAttributeUtils {

}

const bufferOffset = dataOffset * ( isTypedArray ? array.BYTES_PER_ELEMENT : 1 ); // bufferOffset is always in bytes
const bufferOffset = dataOffset * ( isTyped ? array.BYTES_PER_ELEMENT : 1 ); // bufferOffset is always in bytes

device.queue.writeBuffer(
buffer,
Expand Down Expand Up @@ -415,19 +415,6 @@ class WebGPUAttributeUtils {

}

/**
* Returns `true` if the given array is a typed array.
*
* @private
* @param {any} array - The array.
* @return {boolean} Whether the given array is a typed array or not.
*/
_isTypedArray( array ) {

return ArrayBuffer.isView( array ) && ! ( array instanceof DataView );

}

/**
* Utility method for handling interleaved buffer attributes correctly.
* To process them, their `InterleavedBuffer` is returned.
Expand Down
45 changes: 41 additions & 4 deletions src/renderers/webgpu/utils/WebGPUBindingUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {

import { FloatType, IntType, UnsignedIntType } from '../../../constants.js';
import { NodeAccess } from '../../../nodes/core/constants.js';
import { error } from '../../../utils.js';
import { isTypedArray, error } from '../../../utils.js';

/**
* A WebGPU backend utility module for managing bindings.
Expand Down Expand Up @@ -306,10 +306,47 @@ class WebGPUBindingUtils {
const backend = this.backend;
const device = backend.device;

const buffer = binding.buffer;
const bufferGPU = backend.get( binding ).buffer;
const array = binding.buffer; // cpu
const buffer = backend.get( binding ).buffer; // gpu

device.queue.writeBuffer( bufferGPU, 0, buffer, 0 );
const updateRanges = binding.updateRanges;

if ( updateRanges.length === 0 ) {

device.queue.writeBuffer(
buffer,
0,
array,
0
);

} else {

const isTyped = isTypedArray( array );
const byteOffsetFactor = isTyped ? 1 : array.BYTES_PER_ELEMENT;

for ( let i = 0, l = updateRanges.length; i < l; i ++ ) {

const range = updateRanges[ i ];

const dataOffset = range.start * byteOffsetFactor;
const size = range.count * byteOffsetFactor;

const bufferOffset = dataOffset * ( isTyped ? array.BYTES_PER_ELEMENT : 1 ); // bufferOffset is always in bytes

device.queue.writeBuffer(
buffer,
bufferOffset,
array,
dataOffset,
size
);

}

binding.clearUpdateRanges();

}

}

Expand Down
Loading