Structured buffer float compression

Started by
19 comments, last by Hyunkel 11 years, 10 months ago
I have a computer shader that generates procedural planetary terrain and stores vertices in a structured buffer which has the following layout.

struct PlanetVertex
{
float3 Position;
float3 Normal;
float Temperature;
float Humidity;
};


That's 10 floats with 4 bytes per float -> 40 bytes per vertex.
A terrain node or patch contains 33x33 vertices, which is 43560 bytes.
At the highest quality setting, the compute shader will output up to 5000 nodes,
so the buffer needs to be 5000 * 43560 bytes, which is
217800000 bytes or ~207mb.

Due to the way I handle load balancing between rendering and terrain generation, I need to have this buffer in memory twice, so I use
~415mb only for vertex data.
This is okay I guess, since a planet is sort of the primary object, but I want to reduce this buffer size if possible.

For example the normal vector: It doesn't need 32bit precision per channel, 16 would be more than enough.
As for temperature and humidity, they could even fit in an 8bit unorm, but I doubt that's available here.

I found that there are f32tof16 and f16tof32 functions in hlsl, which I assume are what I need, but I cannot quite figure out how they're supposed to work:
http://msdn.microsoft.com/en-us/library/windows/desktop/ff471399(v=vs.85).aspx

It says here that f32to16 returns a uint, but isn't that 32 bit as well?

Cheers,
Hyu
Advertisement
The f16 is stored in the 16 lowest bits of the uint.

Niko Suni

Oh, alright, now I get it.
So basically I have to run f32tof16 on two floats which I want to store in a single uint (with 16 bit precision) but I have to do packaging myself.

uint Float2ToF1616(in float2 f)
{
uint packed;
packed = asuint(f32tof16(f.x)) | (asuint(f32tof16(f.y)) << 16);
return packed;
}


Thanks! :)
If you assume that the normal is a normalized vector, you can pack it to a 2-element vector:

n[sub]z[/sub] = 1.0 - n[sub]x[/sub] - n[sub]y[/sub]

Niko Suni


Oh, alright, now I get it.
So basically I have to run f32tof16 on two floats which I want to store in a single uint (with 16 bit precision) but I have to do packaging myself.

uint Float2ToF1616(in float2 f)
{
uint packed;
packed = asuint(f32tof16(f.x)) | (asuint(f32tof16(f.y)) << 16);
return packed;
}


Thanks! :)


Yes

Niko Suni

The underlying reason for this is that modern hardware doesn't actually have 16-bit registers.

Niko Suni


If you assume that the normal is a normalized vector, you can pack it to a 2-element vector:

n[sub]z[/sub] = 1.0 - n[sub]x[/sub] - n[sub]y[/sub]


The only packing algorithms I know of are these:
http://aras-p.info/texts/CompactNormalStorage.html

and they are for view space normal vectors mostly.
I don't really see a quick way to do 2 channel packing, though it would be quite useful if I could.


The underlying reason for this is that modern hardware doesn't actually have 16-bit registers.


Yeah, I'm aware, which is why I thought that f32tof16() would need to take 2 floats as input and got confused.
The method #1 on that page is what I had in mind. You do need bias the x and y to 0...1 beforehand, which I forgot to mention.

Niko Suni

...and since you bias the x and y to positive range, you can use either's sign to encode the z direction.

Niko Suni


If you assume that the normal is a normalized vector, you can pack it to a 2-element vector:

n[sub]z[/sub] = 1.0 - n[sub]x[/sub] - n[sub]y[/sub]


Did you mean n[sub]z[/sub] = sqrt(1.0 - n[sub]x[/sub][sup]2[/sup] - n[sub]y[/sub][sup]2[/sup]) ?

This topic is closed to new replies.

Advertisement