Pages: 1
Author Topic: Packing bits  (30,485 Views)
Offline (Unknown gender) TheExDeus

Developer
Joined: Apr 2008
Posts: 1,860
View profile
Posted on: October 05, 2014, 04:35:21 PM
I am looking into packing some stuff more compactly for OpenGL, but I have some problems coding that.
For normals, it's recomended to use GL_INT_2_10_10_10_REV​ format, which is basically 10bits per normal (x,y,z) and 2 bits left over. Right now all normals are floats, 32bit per normal. This packing would reduce the size by 2/3. This is how I tried it:
Code (c++) Select
normal_t val = 0.0f;
val = val | (0 << 30);
val = val | ((unsigned int)((nz+1.0f)*0.5f*1023) << 20);
val = val | ((unsigned int)((ny+1.0f)*0.5f*1023) << 10);
val = val | ((unsigned int)((nx+1.0f)*0.5f*1023) << 0);
Where normal_t is just a 32bit structure like color_t is right now:
Code (c++) Select
template<int x> struct intmatch { };
template<int x> struct uintmatch { };
template<> struct intmatch<1>   { typedef int8_t type;  };
template<> struct intmatch<2>   { typedef int16_t type; };
template<> struct intmatch<4>   { typedef int32_t type; };
template<> struct intmatch<8>   { typedef int64_t type; };
template<> struct uintmatch<1>  { typedef uint8_t type;  };
template<> struct uintmatch<2>  { typedef uint16_t type; };
template<> struct uintmatch<4>  { typedef uint32_t type; };
template<> struct uintmatch<8>  { typedef uint64_t type; };
typedef uintmatch<sizeof(gs_scalar)>::type color_t;
typedef intmatch<sizeof(gs_scalar)>::type uv_t;
typedef intmatch<sizeof(gs_scalar)>::type normal_t;
I use 1023, because that is the biggest number you can hold it 10bits. All normals are from -1 to +1, so what I do is offset it (add +1.0) and scale (multiply by 0.5), then I multiply by 1023 and move the bits in place. I don't know how to really unpack it to see if it's correct though. I tried this:
Code (c++) Select
    printf("Normal before packing %f, %f, %f after unpacking %f, %f, %f\n", nx,ny,nz,(double)(val & 1023)/1023.0-1.0,(double)((val & 1023)>>10)/1023.0-1.0,(double)((val & 1023)>>20)/1023.0-1.0);
But it shows 1.0 for ny and nz when it shouldn't. nx is also incorrect. I know there will be a slight loss of precision, but it shouldn't matter for normals.

The second thing that is encouraged to pack is UV coordinates. I see recommendations for them being SHORT. I can't seem to pack them either. Then there is the problem that UV's are not limited to +-1.0. Normally they are, but if you want the texture to repeat, you give values out of this range. If we pack SHORT as in integer, then we cannot have that (as it will normalize and change to +-1.0 when sending to GPU). A half-float could work though. But I don't know how to pack half-floats either.

tl;dr - How to pack 3 floats in 1 float by having 10bit integer each?
How to pack 2 floats in 1 float having 16bit float each?
Offline (Unknown gender) Goombert

Developer
Joined: Jan 2013
Posts: 2,991
View profile
Reply #1 Posted on: October 05, 2014, 10:00:29 PM
Did you look at how we pack color_t? You would also have to change the stride any everything Harri, did you change and fix all of that?
Offline (Unknown gender) TheExDeus

Developer
Joined: Apr 2008
Posts: 1,860
View profile
Reply #2 Posted on: October 06, 2014, 09:40:45 AM
It's a lot easier to pack color_t as they are integers. Here I need to pack half floats, which require a lot more magic. And 10bit integers, which are also "non standard" in C++. I know everything about strides and GL side. The problem is only packing. Making a "float floatToHalfFloatPacked(float u, float v)" function and "float floatTo10bitPacked(float nx, float ny, float nz)". I will figure it out eventually, but I just know people like Josh are really good with bit arithmetic, which I'm not.
Offline (Unknown gender) Goombert

Developer
Joined: Jan 2013
Posts: 2,991
View profile
Reply #3 Posted on: October 06, 2014, 10:30:00 AM
Heh, me neither Harri, I'll try to call this to his attention.
Pages: 1