@@ -42,6 +42,11 @@ function rendererWebGPU(p5, fn) {
4242
4343 this . samplers = new Map ( ) ;
4444
45+ this . uniformBufferAlignment = 256 ;
46+ this . uniformBufferPool = [ ] ;
47+ this . activeUniformBuffers = [ ] ;
48+ this . currentUniformBuffer = undefined ;
49+
4550 // Cache for current frame's canvas texture view
4651 this . currentCanvasColorTexture = null ;
4752 this . currentCanvasColorTextureView = null ;
@@ -600,6 +605,7 @@ function rendererWebGPU(p5, fn) {
600605 // data: firstData,
601606 // dataView: firstDataView
602607 // }],
608+ dynamic : groupUniforms . some ( u => u . name . startsWith ( 'uModel' ) ) ,
603609 buffersInUse : new Set ( ) ,
604610 currentBuffer : null , // For caching
605611 cachedData : null // For caching comparison
@@ -616,9 +622,10 @@ function rendererWebGPU(p5, fn) {
616622 const group0Entries = [ ] ;
617623 for ( const bufferGroup of shader . _uniformBufferGroups ) {
618624 group0Entries . push ( {
625+ bufferGroup,
619626 binding : bufferGroup . binding ,
620627 visibility : GPUShaderStage . VERTEX | GPUShaderStage . FRAGMENT ,
621- buffer : { type : 'uniform' } ,
628+ buffer : { type : 'uniform' , hasDynamicOffset : bufferGroup . dynamic } ,
622629 } ) ;
623630 }
624631 group0Entries . sort ( ( a , b ) => a . binding - b . binding ) ;
@@ -1155,7 +1162,6 @@ function rendererWebGPU(p5, fn) {
11551162 // Uniform buffer pool management
11561163 //////////////////////////////////////////////
11571164
1158- // TODO(dave): delete?
11591165 _getUniformBufferFromPool ( bufferGroup ) {
11601166 // Try to get a buffer from the pool
11611167 if ( bufferGroup . bufferPool . length > 0 ) {
@@ -1171,7 +1177,6 @@ function rendererWebGPU(p5, fn) {
11711177 } ) ;
11721178 const newData = new Float32Array ( bufferGroup . size / 4 ) ;
11731179 const newDataView = new DataView ( newData . buffer ) ;
1174-
11751180 const bufferInfo = {
11761181 buffer : newBuffer ,
11771182 data : newData ,
@@ -1182,6 +1187,39 @@ function rendererWebGPU(p5, fn) {
11821187 return bufferInfo ;
11831188 }
11841189
1190+ _getDynamicUniformBufferFromPool ( bufferGroup ) {
1191+ let buffer ;
1192+ if ( this . currentUniformBuffer && this . currentUniformBuffer . offset + bufferGroup . size < this . currentUniformBuffer . size ) {
1193+ buffer = this . currentUniformBuffer ;
1194+ } else if ( this . uniformBufferPool . length > 0 ) {
1195+ buffer = this . uniformBufferPool . pop ( ) ;
1196+ this . activeUniformBuffers . push ( buffer ) ;
1197+ } else {
1198+ const size = 256 * 10 * 4 ;
1199+ buffer = {
1200+ dynamic : true ,
1201+ lastOffset : 0 ,
1202+ offset : 0 ,
1203+ size,
1204+ buffer : this . device . createBuffer ( {
1205+ size : size ,
1206+ usage : GPUBufferUsage . UNIFORM ,
1207+ mappedAtCreation : true ,
1208+ } ) ,
1209+ }
1210+ this . activeUniformBuffers . push ( buffer ) ;
1211+ }
1212+
1213+ if ( ! buffer . data ) {
1214+ buffer . data = new Float32Array ( buffer . buffer . getMappedRange ( ) ) ;
1215+ buffer . dataView = new DataView ( buffer . data . buffer ) ;
1216+ }
1217+
1218+ this . currentUniformBuffer = buffer ;
1219+
1220+ return buffer ;
1221+ }
1222+
11851223 _returnUniformBuffersToPool ( ) {
11861224 // Return all used buffers back to their pools for all registered shaders
11871225 for ( const shader of this . _shadersWithPools ) {
@@ -1213,9 +1251,16 @@ function rendererWebGPU(p5, fn) {
12131251 this . _pendingCommandEncoders = [ ] ;
12141252 this . _hasPendingDraws = false ;
12151253
1254+ for ( const bufferInfo of this . activeUniformBuffers ) {
1255+ bufferInfo . buffer . unmap ( ) ;
1256+ }
1257+
12161258 // Submit the commands
12171259 this . queue . submit ( commandsToSubmit ) ;
12181260
1261+ this . activeUniformBuffers = [ ] ;
1262+ this . currentUniformBuffer = undefined ;
1263+
12191264 // Execute post-submit callbacks after GPU work completes
12201265 if ( this . _postSubmitCallbacks . length > 0 ) {
12211266 const callbacks = this . _postSubmitCallbacks ;
@@ -1355,83 +1400,83 @@ function rendererWebGPU(p5, fn) {
13551400 this . _uniformBuffersForBinding . clear ( ) ;
13561401
13571402 for ( const bufferGroup of currentShader . _uniformBufferGroups ) {
1358- let bufferInfo ;
1359- const dataChanged = this . _hasGroupDataChanged ( currentShader , bufferGroup ) ;
1360-
1361- if ( ! dataChanged && bufferGroup . currentBuffer ) {
1362- // Reuse the cached buffer - no need to pack or write
1363- bufferInfo = bufferGroup . currentBuffer ;
1364- // Still need to track it in buffersInUse for proper cleanup
1365- bufferGroup . buffersInUse . add ( bufferInfo ) ;
1403+ if ( bufferGroup . dynamic ) {
1404+ // Bind uniforms - get a buffer from the pool
1405+ const uniformBufferInfo = this . _getDynamicUniformBufferFromPool ( bufferGroup ) ;
1406+ this . _packUniformGroup ( currentShader , bufferGroup . uniforms , uniformBufferInfo ) ;
1407+ // this._packUniforms(currentShader, uniformBufferInfo);
1408+ uniformBufferInfo . lastOffset = uniformBufferInfo . offset ;
1409+ uniformBufferInfo . offset += Math . ceil ( bufferGroup . size / this . uniformBufferAlignment ) * this . uniformBufferAlignment ;
1410+ /*this.device.queue.writeBuffer(
1411+ uniformBufferInfo.buffer,
1412+ 0,
1413+ uniformBufferInfo.data.buffer,
1414+ uniformBufferInfo.data.byteOffset,
1415+ uniformBufferInfo.data.byteLength
1416+ );*/
1417+ // Make a shallow copy so that we keep track of the last offset for this uniform
1418+ this . _uniformBuffersForBinding . set ( bufferGroup . binding , { ...uniformBufferInfo } ) ;
13661419 } else {
1367- // Data changed - get a buffer from the pool
1368- bufferInfo = this . _getUniformBufferFromPool ( bufferGroup ) ;
1420+ let bufferInfo ;
1421+ const dataChanged = this . _hasGroupDataChanged ( currentShader , bufferGroup ) ;
1422+
1423+ if ( ! dataChanged && bufferGroup . currentBuffer ) {
1424+ // Reuse the cached buffer - no need to pack or write
1425+ bufferInfo = bufferGroup . currentBuffer ;
1426+ // Still need to track it in buffersInUse for proper cleanup
1427+ bufferGroup . buffersInUse . add ( bufferInfo ) ;
1428+ } else {
1429+ // Data changed - get a buffer from the pool
1430+ bufferInfo = this . _getUniformBufferFromPool ( bufferGroup ) ;
13691431
1370- // Pack and write the data
1371- this . _packUniformGroup ( currentShader , bufferGroup . uniforms , bufferInfo ) ;
1372- this . device . queue . writeBuffer (
1373- bufferInfo . buffer ,
1374- 0 ,
1375- bufferInfo . data . buffer ,
1376- bufferInfo . data . byteOffset ,
1377- bufferInfo . data . byteLength
1378- ) ;
1432+ // Pack and write the data
1433+ this . _packUniformGroup ( currentShader , bufferGroup . uniforms , bufferInfo ) ;
1434+ this . device . queue . writeBuffer (
1435+ bufferInfo . buffer ,
1436+ 0 ,
1437+ bufferInfo . data . buffer ,
1438+ bufferInfo . data . byteOffset ,
1439+ bufferInfo . data . byteLength
1440+ ) ;
13791441
1380- currentShader . buffersDirty = currentShader . buffersDirty || { } ;
1381- currentShader . buffersDirty [ bufferGroup . group + ',' + bufferGroup . binding ] = false ;
1382- /*for (const uniform of bufferGroup.uniforms) {
1383- const fullUniform = currentShader.uniforms[uniform.name];
1384- if (fullUniform) {
1385- fullUniform.dirty = false;
1386- }
1387- }*/
1442+ currentShader . buffersDirty = currentShader . buffersDirty || { } ;
1443+ currentShader . buffersDirty [ bufferGroup . group + ',' + bufferGroup . binding ] = false ;
1444+ /*for (const uniform of bufferGroup.uniforms) {
1445+ const fullUniform = currentShader.uniforms[uniform.name];
1446+ if (fullUniform) {
1447+ fullUniform.dirty = false;
1448+ }
1449+ }*/
13881450
1389- // Cache this buffer and data for next frame
1390- bufferGroup . currentBuffer = bufferInfo ;
1391- }
1451+ // Cache this buffer and data for next frame
1452+ bufferGroup . currentBuffer = bufferInfo ;
1453+ }
13921454
1393- this . _uniformBuffersForBinding . set ( bufferGroup . binding , bufferInfo . buffer ) ;
1455+ this . _uniformBuffersForBinding . set ( bufferGroup . binding , bufferInfo ) ;
1456+ }
13941457 }
13951458
13961459 // Bind sampler/texture uniforms and uniform buffers
13971460 for ( const [ group , entries ] of currentShader . _groupEntries ) {
13981461 const bgEntries = entries . map ( entry => {
13991462 // Check if this is a uniform buffer binding
1400- const uniformBuffer = this . _uniformBuffersForBinding . get ( entry . binding ) ;
1401- if ( uniformBuffer ) {
1463+ const uniformBufferInfo = this . _uniformBuffersForBinding . get ( entry . binding ) ;
1464+ if ( uniformBufferInfo ) {
14021465 return {
14031466 binding : entry . binding ,
1404- resource : { buffer : uniformBuffer } ,
1467+ resource : entry . bufferGroup . dynamic
1468+ ? {
1469+ buffer : uniformBufferInfo . buffer ,
1470+ offset : 0 ,
1471+ size : Math . ceil ( entry . bufferGroup . size / this . uniformBufferAlignment ) * this . uniformBufferAlignment ,
1472+ }
1473+ : { buffer : uniformBufferInfo . buffer } ,
14051474 } ;
14061475 }
14071476
1408- // This must be a texture/sampler entry
1409- if ( ! entry . uniform ) {
1410- console . error ( 'Entry missing uniform field:' , entry , 'uniformBuffersForBinding:' , this . _uniformBuffersForBinding ) ;
1411- throw new Error (
1412- `Bind group entry at binding ${ entry . binding } has no uniform field and is not a uniform buffer!`
1413- ) ;
1414- }
1415-
1416- if ( ! entry . uniform . isSampler ) {
1417- console . error ( 'Non-sampler uniform not handled:' , entry . uniform ) ;
1418- throw new Error (
1419- 'All non-texture/sampler uniforms should be in the uniform struct!'
1420- ) ;
1421- }
1422-
1423- const texture = entry . uniform . type === 'sampler'
1424- ? entry . uniform . textureSource ?. texture
1425- : entry . uniform . texture ;
1426-
1427- if ( ! texture && entry . uniform . type !== 'sampler' ) {
1428- console . warn ( `Texture uniform ${ entry . uniform . name } at binding ${ entry . binding } has no texture! Using empty texture.` , {
1429- uniform : entry . uniform ,
1430- type : entry . uniform . type ,
1431- hasTexture : ! ! entry . uniform . texture ,
1432- texture : entry . uniform . texture
1433- } ) ;
1434- }
1477+ // const texture = entry.uniform.type === 'sampler'
1478+ // ? entry.uniform.textureSource?.texture
1479+ // : entry.uniform.texture;
14351480
14361481 return {
14371482 binding : entry . binding ,
@@ -1446,7 +1491,13 @@ function rendererWebGPU(p5, fn) {
14461491 layout,
14471492 entries : bgEntries ,
14481493 } ) ;
1449- passEncoder . setBindGroup ( group , bindGroup ) ;
1494+ passEncoder . setBindGroup (
1495+ group ,
1496+ bindGroup ,
1497+ entries . map ( e => this . _uniformBuffersForBinding . get ( e . binding ) )
1498+ . filter ( b => b ?. dynamic )
1499+ . map ( b => b . lastOffset )
1500+ ) ;
14501501 }
14511502
14521503 if ( currentShader . shaderType === "fill" ) {
@@ -1488,32 +1539,33 @@ function rendererWebGPU(p5, fn) {
14881539 const data = bufferInfo . data ;
14891540 const dataView = bufferInfo . dataView ;
14901541
1542+ const offset = bufferInfo . offset || 0 ;
14911543 for ( const uniform of groupUniforms ) {
14921544 const fullUniform = shader . uniforms [ uniform . name ] ;
14931545 if ( ! fullUniform || fullUniform . isSampler ) continue ;
14941546
14951547 if ( fullUniform . baseType === 'u32' ) {
14961548 if ( fullUniform . size === 4 ) {
1497- dataView . setUint32 ( fullUniform . offset , fullUniform . _cachedData , true ) ;
1549+ dataView . setUint32 ( offset + fullUniform . offset , fullUniform . _cachedData , true ) ;
14981550 } else {
14991551 const uniformData = fullUniform . _cachedData ;
15001552 for ( let i = 0 ; i < uniformData . length ; i ++ ) {
1501- dataView . setUint32 ( fullUniform . offset + i * 4 , uniformData [ i ] , true ) ;
1553+ dataView . setUint32 ( offset + fullUniform . offset + i * 4 , uniformData [ i ] , true ) ;
15021554 }
15031555 }
15041556 } else if ( fullUniform . baseType === 'i32' ) {
15051557 if ( fullUniform . size === 4 ) {
1506- dataView . setInt32 ( fullUniform . offset , fullUniform . _cachedData , true ) ;
1558+ dataView . setInt32 ( offset + fullUniform . offset , fullUniform . _cachedData , true ) ;
15071559 } else {
15081560 const uniformData = fullUniform . _cachedData ;
15091561 for ( let i = 0 ; i < uniformData . length ; i ++ ) {
1510- dataView . setInt32 ( fullUniform . offset + i * 4 , uniformData [ i ] , true ) ;
1562+ dataView . setInt32 ( offset + fullUniform . offset + i * 4 , uniformData [ i ] , true ) ;
15111563 }
15121564 }
15131565 } else if ( fullUniform . size === 4 ) {
1514- data . set ( [ fullUniform . _cachedData ] , fullUniform . offset / 4 ) ;
1566+ data . set ( [ fullUniform . _cachedData ] , ( offset + fullUniform . offset ) / 4 ) ;
15151567 } else if ( fullUniform . _cachedData !== undefined ) {
1516- data . set ( fullUniform . _cachedData , fullUniform . offset / 4 ) ;
1568+ data . set ( fullUniform . _cachedData , ( offset + fullUniform . offset ) / 4 ) ;
15171569 }
15181570 }
15191571 }
@@ -1571,42 +1623,44 @@ function rendererWebGPU(p5, fn) {
15711623 return false ; // No changes detected
15721624 }
15731625
1626+ // TODO: delete
15741627 _packUniforms ( shader , bufferInfo ) {
15751628 const data = bufferInfo . data ;
15761629 const dataView = bufferInfo . dataView ;
15771630
1631+ const offset = bufferInfo . offset ;
15781632 for ( const name in shader . uniforms ) {
15791633 const uniform = shader . uniforms [ name ] ;
15801634 if ( uniform . isSampler ) continue ;
15811635
15821636 if ( uniform . baseType === 'u32' ) {
15831637 if ( uniform . size === 4 ) {
15841638 // Single u32
1585- dataView . setUint32 ( uniform . offset , uniform . _cachedData , true ) ;
1639+ dataView . setUint32 ( offset + uniform . offset , uniform . _cachedData , true ) ;
15861640 } else {
15871641 // Vector of u32s
15881642 const uniformData = uniform . _cachedData ;
15891643 for ( let i = 0 ; i < uniformData . length ; i ++ ) {
1590- dataView . setUint32 ( uniform . offset + i * 4 , uniformData [ i ] , true ) ;
1644+ dataView . setUint32 ( offset + uniform . offset + i * 4 , uniformData [ i ] , true ) ;
15911645 }
15921646 }
15931647 } else if ( uniform . baseType === 'i32' ) {
15941648 if ( uniform . size === 4 ) {
15951649 // Single i32
1596- dataView . setInt32 ( uniform . offset , uniform . _cachedData , true ) ;
1650+ dataView . setInt32 ( offset + uniform . offset , uniform . _cachedData , true ) ;
15971651 } else {
15981652 // Vector of i32s
15991653 const uniformData = uniform . _cachedData ;
16001654 for ( let i = 0 ; i < uniformData . length ; i ++ ) {
1601- dataView . setInt32 ( uniform . offset + i * 4 , uniformData [ i ] , true ) ;
1655+ dataView . setInt32 ( offset + uniform . offset + i * 4 , uniformData [ i ] , true ) ;
16021656 }
16031657 }
16041658 } else if ( uniform . size === 4 ) {
16051659 // Single float value
1606- data . set ( [ uniform . _cachedData ] , uniform . offset / 4 ) ;
1660+ data . set ( [ uniform . _cachedData ] , ( offset + uniform . offset ) / 4 ) ;
16071661 } else if ( uniform . _cachedData !== undefined ) {
16081662 // Float array (including vec2<f32>, vec3<f32>, vec4<f32>, mat4x4<f32>)
1609- data . set ( uniform . _cachedData , uniform . offset / 4 ) ;
1663+ data . set ( uniform . _cachedData , ( offset + uniform . offset ) / 4 ) ;
16101664 }
16111665 }
16121666 }
@@ -3116,6 +3170,7 @@ ${hookUniformFields}}
31163170 * Copy framebuffer content directly to WebGPU texture mip level
31173171 */
31183172 _accumulateMipLevel ( framebuffer , mipmapData , mipLevel , width , height ) {
3173+ this . flushDraw ( ) ;
31193174 // Copy from framebuffer texture to the mip level
31203175 const commandEncoder = this . device . createCommandEncoder ( ) ;
31213176
0 commit comments