Created
February 13, 2026 02:30
-
-
Save sketchpunk/6c3a4276c1f39bc1bcbafccd44ec5f7e to your computer and use it in GitHub Desktop.
Ubo object that handles STD140 layouts, single buffer, type array views wrapped in a proxy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| const ubo = new Ubo([ | |
| { name:'time', type:GTYPE.f32, init:22 }, | |
| { name:'num', type:GTYPE.f32, init:100 }, | |
| { name:'vec', type:GTYPE.vec3, init:[1,2,3] }, | |
| ]); | |
| ubo.time = 999; | |
| ubo.vec = [5,6,7]; | |
| console.log( ubo.time ); | |
| console.log( ubo.num ); | |
| console.log( ubo.vec ); | |
| console.log( ubo._buf ); | |
| */ | |
| const GTYPE = { | |
| f32 : 0, | |
| i32 : 1, | |
| vec2 : 2, | |
| vec3 : 3, | |
| vec4 : 4, | |
| mat4 : 5, | |
| }; | |
| const GTYPEDEF = [ // Stride is 16 when in an array | |
| { size: 4, align: 4, stride: 16, elm: 1, tary:Float32Array }, // f32 | |
| { size: 4, align: 4, stride: 16, elm: 1, tary:Int32Array }, // i32 | |
| { size: 8, align: 8, stride: 16, elm: 2, tary:Float32Array }, // vec2 | |
| { size: 12, align: 16, stride: 16, elm: 3, tary:Float32Array }, // vec3 | |
| { size: 16, align: 16, stride: 16, elm: 4, tary:Float32Array }, // vec4 | |
| { size: 64, align: 16, stride: 64, elm: 16, tary:Float32Array }, // mat4 | |
| ]; | |
| class Ubo{ | |
| _items = {}; | |
| _buf = null; | |
| constructor( cfg ){ | |
| this._build( cfg ); | |
| // Wrap Ubo in a Proxy | |
| return new Proxy( this, { | |
| get: ( tar, prop, receiver )=>{ | |
| const v = tar._getValue( prop ); | |
| return ( v !== undefined )? v | |
| : ( prop in tar )? tar[ prop ] : undefined; // Internal Properties of object | |
| }, | |
| set: ( tar, prop, value, receiver )=>{ | |
| tar._setValue( prop, value ); | |
| return true; | |
| }, | |
| }); | |
| } | |
| _build( cfg ){ | |
| let offset = 0; | |
| let isAry = false; | |
| let def; | |
| let align; | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| for( const i of cfg ){ | |
| def = GTYPEDEF[ i.type ]; | |
| isAry = ( i?.count > 1 ); | |
| // Less then 16 will align to 16 when array | |
| align = !isAry? def.align : Math.max( def.align, 16 ); | |
| // STD140 - Add padding for arrays or F32:Vec3 issue | |
| // F32 > Vec3 :: 4/16 = ceil(0.25) = 1 * 16 = 16 starting position for vec3, adds 12b padding to f32 | |
| // Vec3 > F32 :: 12/4 = ceil(3) = 3 * 4 = 12 starting position for f32, no padding | |
| offset = Math.ceil( offset / align ) * align; | |
| this._items[ i.name ] = { | |
| type : i.type, | |
| offset : offset, | |
| isArray : isAry, | |
| size : def.size, | |
| stride : !isAry ? def.size : Math.max( def.size, def.stride ), | |
| count : i?.count || 1, | |
| view : null, | |
| }; | |
| offset += !isAry ? def.size | |
| : def.stride * (i.count-1) + def.size; | |
| } | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // CREATE BUFFER & VIEWS | |
| const bsize = Math.ceil( offset / 16 ) * 16; | |
| this._buf = new ArrayBuffer( bsize ); | |
| let itm; | |
| for( const i of cfg ){ | |
| itm = this._items[ i.name ]; | |
| def = GTYPEDEF[ itm.type ]; | |
| itm.view = new def.tary( this._buf, itm.offset, def.elm * itm.count ); | |
| if( i.init != null ) this._setValue( i.name, i.init ); | |
| } | |
| } | |
| _setValue( name, value ){ | |
| const itm = this._items[ name ]; | |
| if( !itm ) return; | |
| switch( itm.type ){ | |
| case GTYPE.f32: | |
| case GTYPE.i32 : itm.view[0] = value; return; | |
| default : itm.view.set( value ); | |
| } | |
| return this; | |
| } | |
| _getValue( name ){ | |
| const itm = this._items[ name ]; | |
| if( !itm ) return undefined; | |
| switch( itm.type ){ | |
| case GTYPE.f32: | |
| case GTYPE.i32 : return itm.view[ 0 ]; | |
| default : return itm.view; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment