2 Bitpacking methods
February 9, 2022 ยท View on GitHub
// Method 1:
// by MerlinVR // You can convert the uints to full precision floats using asfloat(half3ToUint(input)) and store floats using uintToHalf3(asuint(inputfloat))
// Packing/unpacking routines for saving integers to R16G16B16A16_FLOAT textures // Heavily based off of https://github.com/apitrace/dxsdk/blob/master/Include/d3dx_dxgiformatconvert.inl // For some reason the last 2 bits get stomped so we'll only allow uint14 for now :( float uint14ToFloat(uint input) { precise float output = (f16tof32((input & 0x00003fff))); return output; }
uint floatToUint14(precise float input) { uint output = (f32tof16(input)) & 0x00003fff; return output; }
// Encodes a 32 bit uint into 3 half precision floats float3 uintToHalf3(uint input) { precise float3 output = float3(uint14ToFloat(input), uint14ToFloat(input >> 14), uint14ToFloat((input >> 28) & 0x0000000f)); return output; }
uint half3ToUint(precise float3 input) { return floatToUint14(input.x) | (floatToUint14(input.y) << 14) | ((floatToUint14(input.z) & 0x0000000f) << 28); }
//Method 2:
//Using these 2 functions to bit pack four 8bit variables into a single 32bit variable uint PackByteQ(uint byte1, uint byte2, uint byte3, uint byte4) { return (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4; }
uint4 UnpackByteQ(uint byteQ) { return uint4(byteQ >> 24, (byteQ >> 16) & 0xff, (byteQ >> 8) & 0xff, (byteQ) & 0xff); }
//These could be changed to pack and unpack different data widths //Example using 8bit variables: struct v2f_example { float4 vertex : SV_POSITION; //... // Any pre-required properties uint4 byteQx4_1 : TEXCOORD1; uint4 byteQx4_2 : TEXCOORD2; //... // uint4 byteQx4_Nth : TEXCOORDNth; };
//Inside v2f vert() //v2f o; //...
// Pack 16 8bit variables into a 128bit buffer
// Values are set somehow // uint var1 = 0x11; // uint var2 = 0x22; // uint var3 = 0x33; // uint var4 = 0x44; // uint var5 = 0x55; // uint var6 = 0x66; // uint var7 = 0x77; // uint var8 = 0x88; // uint var9 = 0x99; // uint var10 = 0xaa; // uint var11 = 0xbb; // uint var12 = 0xcc; // uint var13 = 0xdd; // uint var14 = 0xee; // uint var15 = 0xff; // uint var16 = 0x00; // Values are assumed to be less than 255 // (This could be asserted by masking values with 255) // eg: // var1 &= 0xff;
// uint varQ1 = PackByteQ(var1, var2, var3, var4); // uint varQ2 = PackByteQ(var5, var6, var7, var8); // uint varQ3 = PackByteQ(var9, var10, var11, var12); // uint varQ4 = PackByteQ(var13, var14, var15, var16);
// o.byteQx4_1 = uint4(varQ1, varQ2, varQ3, varQ4);
//...
// Inside frag(v2f in) // ...
// Unpack 16 8bit variables out of a 128bit buffer // uint4 ByteQ1 = UnpackByteQ(in.byteQx4_1.x); // uint4 ByteQ2 = UnpackByteQ(in.byteQx4_1.y); // uint4 ByteQ3 = UnpackByteQ(in.byteQx4_1.y); // uint4 ByteQ4 = UnpackByteQ(in.byteQx4_1.z);
// uint var1 = ByteQ1.x; // uint var2 = ByteQ1.y; // uint var3 = ByteQ1.w; // uint var4 = ByteQ1.z; // uint var5 = ByteQ2.x; // uint var6 = ByteQ2.y; // uint var7 = ByteQ2.w; // uint var8 = ByteQ2.z; // uint var9 = ByteQ3.x; // uint var10 = ByteQ3.y; // uint var11 = ByteQ3.w; // uint var12 = ByteQ3.z; // uint var13 = ByteQ4.x; // uint var14 = ByteQ4.y; // uint var15 = ByteQ4.w; // uint var16 = ByteQ4.z;
//...
// Here are some functions that can also pack 2 floats into one 32bit variable: // This packer reduces the mantissa size, affecting percision of smaller numbers (non IEEE-754 standard) // Can also result in some values of NaN becoming Inf or -Inf uint PackHalf2(float x, float y) { return (asuint(x) & 0xffff0000) | (asuint(y) >> 16); } float2 UnpackHalf2(uint pair) { return float2(asfloat(pair & 0xffff0000), asfloat(pair << 16)); }