in src/webgpu/util/texture/texel_data.ts [167:281]
function packComponents(
componentOrder: TexelComponent[],
components: PerTexelComponent<number>,
bitLengths: number | PerTexelComponent<number>,
componentDataTypes: ComponentDataType | PerTexelComponent<ComponentDataType>
): ArrayBuffer {
let bitLengthMap;
let totalBitLength;
if (typeof bitLengths === 'number') {
bitLengthMap = makePerTexelComponent(componentOrder, bitLengths);
totalBitLength = bitLengths * componentOrder.length;
} else {
bitLengthMap = bitLengths;
totalBitLength = Object.entries(bitLengthMap).reduce((acc, [, value]) => {
assert(value !== undefined);
return acc + value;
}, 0);
}
assert(totalBitLength % 8 === 0);
const componentDataTypeMap =
typeof componentDataTypes === 'string' || componentDataTypes === null
? makePerTexelComponent(componentOrder, componentDataTypes)
: componentDataTypes;
const dataView = getComponentDataView(totalBitLength / 8);
let bitOffset = 0;
for (const c of componentOrder) {
const value = components[c];
const type = componentDataTypeMap[c];
const bitLength = bitLengthMap[c];
assert(value !== undefined);
assert(type !== undefined);
assert(bitLength !== undefined);
const byteOffset = Math.floor(bitOffset / 8);
const byteLength = Math.ceil(bitLength / 8);
switch (type) {
case 'uint':
case 'unorm':
if (byteOffset === bitOffset / 8 && byteLength === bitLength / 8) {
switch (byteLength) {
case 1:
dataView.setUint8(byteOffset, value);
break;
case 2:
dataView.setUint16(byteOffset, value, true);
break;
case 4:
dataView.setUint32(byteOffset, value, true);
break;
default:
unreachable();
}
} else {
// Packed representations are all 32-bit and use Uint as the data type.
// ex.) rg10b11float, rgb10a2unorm
switch (dataView.byteLength) {
case 4: {
const currentValue = dataView.getUint32(0, true);
let mask = 0xffffffff;
const bitsToClearRight = bitOffset;
const bitsToClearLeft = 32 - (bitLength + bitOffset);
mask = (mask >>> bitsToClearRight) << bitsToClearRight;
mask = (mask << bitsToClearLeft) >>> bitsToClearLeft;
const newValue = (currentValue & ~mask) | (value << bitOffset);
dataView.setUint32(0, newValue, true);
break;
}
default:
unreachable();
}
}
break;
case 'sint':
case 'snorm':
assert(byteOffset === bitOffset / 8 && byteLength === bitLength / 8);
switch (byteLength) {
case 1:
dataView.setInt8(byteOffset, value);
break;
case 2:
dataView.setInt16(byteOffset, value, true);
break;
case 4:
dataView.setInt32(byteOffset, value, true);
break;
default:
unreachable();
}
break;
case 'float':
assert(byteOffset === bitOffset / 8 && byteLength === bitLength / 8);
switch (byteLength) {
case 4:
dataView.setFloat32(byteOffset, value, true);
break;
default:
unreachable();
}
break;
case 'ufloat':
case null:
unreachable();
}
bitOffset += bitLength;
}
return dataView.buffer;
}