123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- function WebGLUniformsGroups( gl, info, capabilities, state ) {
- let buffers = {};
- let updateList = {};
- let allocatedBindingPoints = [];
- const maxBindingPoints = ( capabilities.isWebGL2 ) ? gl.getParameter( gl.MAX_UNIFORM_BUFFER_BINDINGS ) : 0; // binding points are global whereas block indices are per shader program
- function bind( uniformsGroup, program ) {
- const webglProgram = program.program;
- state.uniformBlockBinding( uniformsGroup, webglProgram );
- }
- function update( uniformsGroup, program ) {
- let buffer = buffers[ uniformsGroup.id ];
- if ( buffer === undefined ) {
- prepareUniformsGroup( uniformsGroup );
- buffer = createBuffer( uniformsGroup );
- buffers[ uniformsGroup.id ] = buffer;
- uniformsGroup.addEventListener( 'dispose', onUniformsGroupsDispose );
- }
- // ensure to update the binding points/block indices mapping for this program
- const webglProgram = program.program;
- state.updateUBOMapping( uniformsGroup, webglProgram );
- // update UBO once per frame
- const frame = info.render.frame;
- if ( updateList[ uniformsGroup.id ] !== frame ) {
- updateBufferData( uniformsGroup );
- updateList[ uniformsGroup.id ] = frame;
- }
- }
- function createBuffer( uniformsGroup ) {
- // the setup of an UBO is independent of a particular shader program but global
- const bindingPointIndex = allocateBindingPointIndex();
- uniformsGroup.__bindingPointIndex = bindingPointIndex;
- const buffer = gl.createBuffer();
- const size = uniformsGroup.__size;
- const usage = uniformsGroup.usage;
- gl.bindBuffer( gl.UNIFORM_BUFFER, buffer );
- gl.bufferData( gl.UNIFORM_BUFFER, size, usage );
- gl.bindBuffer( gl.UNIFORM_BUFFER, null );
- gl.bindBufferBase( gl.UNIFORM_BUFFER, bindingPointIndex, buffer );
- return buffer;
- }
- function allocateBindingPointIndex() {
- for ( let i = 0; i < maxBindingPoints; i ++ ) {
- if ( allocatedBindingPoints.indexOf( i ) === - 1 ) {
- allocatedBindingPoints.push( i );
- return i;
- }
- }
- console.error( 'THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.' );
- return 0;
- }
- function updateBufferData( uniformsGroup ) {
- const buffer = buffers[ uniformsGroup.id ];
- const uniforms = uniformsGroup.uniforms;
- const cache = uniformsGroup.__cache;
- gl.bindBuffer( gl.UNIFORM_BUFFER, buffer );
- for ( let i = 0, il = uniforms.length; i < il; i ++ ) {
- const uniform = uniforms[ i ];
- // partly update the buffer if necessary
- if ( hasUniformChanged( uniform, i, cache ) === true ) {
- const offset = uniform.__offset;
- const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ];
- let arrayOffset = 0;
- for ( let i = 0; i < values.length; i ++ ) {
- const value = values[ i ];
- const info = getUniformSize( value );
- if ( typeof value === 'number' || typeof value === 'boolean' ) {
- uniform.__data[ 0 ] = value;
- gl.bufferSubData( gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data );
- } else if ( value.isMatrix3 ) {
- // manually converting 3x3 to 3x4
- uniform.__data[ 0 ] = value.elements[ 0 ];
- uniform.__data[ 1 ] = value.elements[ 1 ];
- uniform.__data[ 2 ] = value.elements[ 2 ];
- uniform.__data[ 3 ] = 0;
- uniform.__data[ 4 ] = value.elements[ 3 ];
- uniform.__data[ 5 ] = value.elements[ 4 ];
- uniform.__data[ 6 ] = value.elements[ 5 ];
- uniform.__data[ 7 ] = 0;
- uniform.__data[ 8 ] = value.elements[ 6 ];
- uniform.__data[ 9 ] = value.elements[ 7 ];
- uniform.__data[ 10 ] = value.elements[ 8 ];
- uniform.__data[ 11 ] = 0;
- } else {
- value.toArray( uniform.__data, arrayOffset );
- arrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT;
- }
- }
- gl.bufferSubData( gl.UNIFORM_BUFFER, offset, uniform.__data );
- }
- }
- gl.bindBuffer( gl.UNIFORM_BUFFER, null );
- }
- function hasUniformChanged( uniform, index, cache ) {
- const value = uniform.value;
- if ( cache[ index ] === undefined ) {
- // cache entry does not exist so far
- if ( typeof value === 'number' || typeof value === 'boolean' ) {
- cache[ index ] = value;
- } else {
- const values = Array.isArray( value ) ? value : [ value ];
- const tempValues = [];
- for ( let i = 0; i < values.length; i ++ ) {
- tempValues.push( values[ i ].clone() );
- }
- cache[ index ] = tempValues;
- }
- return true;
- } else {
- // compare current value with cached entry
- if ( typeof value === 'number' || typeof value === 'boolean' ) {
- if ( cache[ index ] !== value ) {
- cache[ index ] = value;
- return true;
- }
- } else {
- const cachedObjects = Array.isArray( cache[ index ] ) ? cache[ index ] : [ cache[ index ] ];
- const values = Array.isArray( value ) ? value : [ value ];
- for ( let i = 0; i < cachedObjects.length; i ++ ) {
- const cachedObject = cachedObjects[ i ];
- if ( typeof cachedObject === 'number' || typeof cachedObject === 'boolean' ) {
- if ( cachedObject !== values[ i ] ) {
- cachedObjects[ i ] = values[ i ];
- return true;
- }
- } else if ( cachedObject.equals( values[ i ] ) === false ) {
- cachedObject.copy( values[ i ] );
- return true;
- }
- }
- }
- }
- return false;
- }
- function prepareUniformsGroup( uniformsGroup ) {
- // determine total buffer size according to the STD140 layout
- // Hint: STD140 is the only supported layout in WebGL 2
- const uniforms = uniformsGroup.uniforms;
- let offset = 0; // global buffer offset in bytes
- const chunkSize = 16; // size of a chunk in bytes
- let chunkOffset = 0; // offset within a single chunk in bytes
- for ( let i = 0, l = uniforms.length; i < l; i ++ ) {
- const uniform = uniforms[ i ];
- const infos = {
- boundary: 0, // bytes
- storage: 0 // bytes
- };
- const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ];
- for ( let j = 0, jl = values.length; j < jl; j ++ ) {
- const value = values[ j ];
- const info = getUniformSize( value );
- infos.boundary += info.boundary;
- infos.storage += info.storage;
- }
- // the following two properties will be used for partial buffer updates
- uniform.__data = new Float32Array( infos.storage / Float32Array.BYTES_PER_ELEMENT );
- uniform.__offset = offset;
- //
- if ( i > 0 ) {
- chunkOffset = offset % chunkSize;
- const remainingSizeInChunk = chunkSize - chunkOffset;
- // check for chunk overflow
- if ( chunkOffset !== 0 && ( remainingSizeInChunk - infos.boundary ) < 0 ) {
- // add padding and adjust offset
- offset += ( chunkSize - chunkOffset );
- uniform.__offset = offset;
- }
- }
- offset += infos.storage;
- }
- // ensure correct final padding
- chunkOffset = offset % chunkSize;
- if ( chunkOffset > 0 ) offset += ( chunkSize - chunkOffset );
- //
- uniformsGroup.__size = offset;
- uniformsGroup.__cache = {};
- return this;
- }
- function getUniformSize( value ) {
- const info = {
- boundary: 0, // bytes
- storage: 0 // bytes
- };
- // determine sizes according to STD140
- if ( typeof value === 'number' || typeof value === 'boolean' ) {
- // float/int/bool
- info.boundary = 4;
- info.storage = 4;
- } else if ( value.isVector2 ) {
- // vec2
- info.boundary = 8;
- info.storage = 8;
- } else if ( value.isVector3 || value.isColor ) {
- // vec3
- info.boundary = 16;
- info.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes
- } else if ( value.isVector4 ) {
- // vec4
- info.boundary = 16;
- info.storage = 16;
- } else if ( value.isMatrix3 ) {
- // mat3 (in STD140 a 3x3 matrix is represented as 3x4)
- info.boundary = 48;
- info.storage = 48;
- } else if ( value.isMatrix4 ) {
- // mat4
- info.boundary = 64;
- info.storage = 64;
- } else if ( value.isTexture ) {
- console.warn( 'THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group.' );
- } else {
- console.warn( 'THREE.WebGLRenderer: Unsupported uniform value type.', value );
- }
- return info;
- }
- function onUniformsGroupsDispose( event ) {
- const uniformsGroup = event.target;
- uniformsGroup.removeEventListener( 'dispose', onUniformsGroupsDispose );
- const index = allocatedBindingPoints.indexOf( uniformsGroup.__bindingPointIndex );
- allocatedBindingPoints.splice( index, 1 );
- gl.deleteBuffer( buffers[ uniformsGroup.id ] );
- delete buffers[ uniformsGroup.id ];
- delete updateList[ uniformsGroup.id ];
- }
- function dispose() {
- for ( const id in buffers ) {
- gl.deleteBuffer( buffers[ id ] );
- }
- allocatedBindingPoints = [];
- buffers = {};
- updateList = {};
- }
- return {
- bind: bind,
- update: update,
- dispose: dispose
- };
- }
- export { WebGLUniformsGroups };
|