UNPKG

@absulit/points

Version:

A Generative Art library made in WebGPU

324 lines (287 loc) 10.7 kB
/** * Utility types and methods to set wgsl types in memory. * This is mainly internal. * @module data-size * @ignore */ const size_4_align_4 = { size: 4, align: 4 }; const size_8_align_8 = { size: 8, align: 8 }; const size_12_align_16 = { size: 12, align: 16 }; const size_16_align_16 = { size: 16, align: 16 }; const size_16_align_8 = { size: 16, align: 8 }; const size_32_align_8 = { size: 32, align: 8 }; const size_24_align_16 = { size: 24, align: 16 }; const size_48_align_16 = { size: 48, align: 16 }; const size_32_align_16 = { size: 32, align: 16 }; const size_64_align_16 = { size: 64, align: 16 }; export const typeSizes = { 'bool': size_4_align_4, 'f32': size_4_align_4, 'i32': size_4_align_4, 'u32': size_4_align_4, 'vec2<bool>': size_8_align_8, 'vec2<f32>': size_8_align_8, 'vec2<i32>': size_8_align_8, 'vec2<u32>': size_8_align_8, // 'vec2<bool>': size_8_align_8, 'vec2f': size_8_align_8, 'vec2i': size_8_align_8, 'vec2u': size_8_align_8, 'vec3<bool>': size_12_align_16, 'vec3<f32>': size_12_align_16, 'vec3<i32>': size_12_align_16, 'vec3<u32>': size_12_align_16, // 'vec3<bool>': size_12_align_16, 'vec3f': size_12_align_16, 'vec3i': size_12_align_16, 'vec3u': size_12_align_16, 'vec4<bool>': size_16_align_16, 'vec4<f32>': size_16_align_16, 'vec4<i32>': size_16_align_16, 'vec4<u32>': size_16_align_16, 'mat2x2<f32>': size_16_align_8, 'mat2x3<f32>': size_32_align_8, 'mat2x4<f32>': size_32_align_8, 'mat3x2<f32>': size_24_align_16, 'mat3x3<f32>': size_48_align_16, 'mat3x4<f32>': size_48_align_16, 'mat4x2<f32>': size_32_align_16, 'mat4x3<f32>': size_64_align_16, 'mat4x4<f32>': size_64_align_16, // 'vec4<bool>': size_16_align_16, 'vec4f': size_16_align_16, 'vec4i': size_16_align_16, 'vec4u': size_16_align_16, 'mat2x2f': size_16_align_8, 'mat2x3f': size_32_align_8, 'mat2x4f': size_32_align_8, 'mat3x2f': size_24_align_16, 'mat3x3f': size_48_align_16, 'mat3x4f': size_48_align_16, 'mat4x2f': size_32_align_16, 'mat4x3f': size_64_align_16, 'mat4x4f': size_64_align_16, } // ignore comments const removeCommentsRE = /^(?:(?!\/\/|\/*.*\/).|\n)+/gim // struct name: const getStructNameRE = /struct\s+?(\w+)\s*{[^}]+}\n?/g // what's inside a struct: const insideStructRE = /struct\s+?\w+\s*{([^}]+)}\n?/g const arrayTypeAndAmountRE = /\s*<\s*([^,]+)\s*,?\s*(\d+)?\s*>/g const arrayIntegrityRE = /\s*(array\s*<\s*\w+\s*(?:,\s*\d+)?\s*>)\s*,?/g // you have to separete the result by splitting new lines function removeComments(value) { const matches = value.matchAll(removeCommentsRE); let result = ''; for (const match of matches) { const captured = match[0]; result += captured; } return result; } function getInsideStruct(value) { const matches = value.matchAll(insideStructRE); let lines = null; for (const match of matches) { lines = match[1].split('\n'); lines = lines.map(element => element.trim()) .filter(e => e !== ''); } return lines; } function getStructDataByName(value) { const matches = value.matchAll(getStructNameRE); let result = new Map(); for (const match of matches) { const captured = match[0]; const name = match[1]; const lines = getInsideStruct(captured); const types = lines.map(l => { const right = l.split(':')[1]; let type = ''; if (isArray(right)) { const arrayMatch = right.matchAll(arrayIntegrityRE); type = arrayMatch.next().value[1]; } else { type = right.split(',')[0].trim(); } return type; }); const names = lines.map(l => { const left = l.split(':')[0]; let name = ''; name = left.split(',')[0].trim(); return name; }); result.set(name, { captured, lines, types, unique_types: [...new Set(types)], names, }); } return result; } export function getArrayTypeAndAmount(value) { const matches = value.matchAll(arrayTypeAndAmountRE); let result = []; for (const match of matches) { const type = match[1]; const amount = match[2]; result.push({ type, amount: Number(amount) }); } return result; } function addBytesToAlign(bytes, aligment) { const remainder = bytes % aligment; let result = 0; if (remainder !== 0) { // if not multiple we obtain the diff // and add it to byteCounter to make it fit the alignment const multipleDiff = aligment - remainder; result = bytes = multipleDiff; } return result; } function getPadding(bytes, aligment, nextTypeDataSize) { const nextMultiple = (bytes + aligment - 1) & ~(aligment - 1) const needsPadding = (bytes + nextTypeDataSize) > nextMultiple; let padAmount = 0 if (needsPadding) { padAmount = nextMultiple - bytes; } return padAmount; } /** * Check if string has 'array' in it * @param {String} value * @returns {boolean} */ export function isArray(value) { return value.indexOf('array') != -1; } export function getArrayAlign(structName, structData) { const [d] = getArrayTypeAndAmount(structName); const t = typeSizes[d.type] || structData.get(d.type); if (!t) { throw new Error(`${d.type} type has not been declared previously`) } // if it's not in typeSizes is a struct, //therefore probably stored in structData return t.align || t.maxAlign; } export function getArrayTypeData(currentType, structData) { const [d] = getArrayTypeAndAmount(currentType); if(!d){ throw `${currentType} seems to have an error, maybe a wrong amount?`; } if (d.amount == 0) { throw new Error(`${currentType} has an amount of 0`); } // if is an array with no amount then use these default values let currentTypeData = { size: 16, align: 16 }; if (!!d.amount) { const t = typeSizes[d.type]; if (t) { // if array, the size is equal to the align currentTypeData = { size: t.align * d.amount, align: t.align }; // currentTypeData = { size: t.size * d.amount, align: t.align }; // currentTypeData = { size: 0, align: 0 }; } else { const sd = structData.get(d.type); if (sd) { currentTypeData = { size: sd.bytes * d.amount, align: sd.maxAlign }; } } } else { const t = typeSizes[d.type] || structData.get(d.type); currentTypeData = { size: t.size || t.bytes, align: t.maxAlign }; } return currentTypeData; } export const dataSize = value => { const noCommentsValue = removeComments(value); const structData = getStructDataByName(noCommentsValue); for (const [structDatumKey, structDatum] of structData) { // to obtain the higher max alignment, but this can be also calculated // in the next step structDatum.unique_types.forEach(ut => { let maxAlign = structDatum.maxAlign || 0; let align = 0; // if it doesn't exists in typeSizes is an Array or a new Struct if (!typeSizes[ut]) { if (isArray(ut)) { align = getArrayAlign(ut, structData); } else { const sd = structData.get(ut); align = sd.maxAlign; } } else { align = typeSizes[ut].align; } maxAlign = align > maxAlign ? align : maxAlign; structDatum.maxAlign = maxAlign; }); let byteCounter = 0; structDatum.types.forEach((t, i) => { const name = structDatum.names[i]; const currentType = t; const nextType = structDatum.types[i + 1]; let currentTypeData = typeSizes[currentType]; let nextTypeData = typeSizes[nextType]; structDatum.paddings = structDatum.paddings || {}; // if currentTypeData or nextTypeData have no data it means // it's a struct or an array // if it's a struct the data is already saved in structData // because it was calculated previously // assuming the struct was declared previously if (!currentTypeData) { if (currentType) { if (isArray(currentType)) { currentTypeData = getArrayTypeData(currentType, structData); } else { const sd = structData.get(currentType); if (sd) { currentTypeData = { size: sd.bytes, align: sd.maxAlign }; } } } } // read above if (!nextTypeData) { if (nextType) { if (isArray(nextType)) { nextTypeData = getArrayTypeData(nextType, structData); } else { const sd = structData.get(nextType) if (sd) { nextTypeData = { size: sd.bytes, align: sd.maxAlign }; } } } } if (!!currentTypeData) { byteCounter += currentTypeData.size; if ((currentTypeData.size === structDatum.maxAlign) || !nextType) { return; } } if (!!nextTypeData) { const padAmount = getPadding(byteCounter, structDatum.maxAlign, nextTypeData.size) if (padAmount) { structDatum.paddings[name] = padAmount / 4; byteCounter += padAmount; } } }); const padAmount = getPadding(byteCounter, structDatum.maxAlign, 16) if (padAmount) { structDatum.paddings[''] = padAmount / 4; byteCounter += padAmount; } structDatum.bytes = byteCounter; } return structData; }