Skip to content

Instantly share code, notes, and snippets.

@sketchpunk
Created February 13, 2026 02:30
Show Gist options
  • Select an option

  • Save sketchpunk/6c3a4276c1f39bc1bcbafccd44ec5f7e to your computer and use it in GitHub Desktop.

Select an option

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.
/*
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