Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


Pack two 8 bits into a 16 bits channel _ HLSL


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
5 replies to this topic

#1 BlackBrain   Members   -  Reputation: 378

Like
0Likes
Like

Posted 15 January 2014 - 10:28 AM

Hey .

 

I am using a R16G16B16A16_Float format for my render target. I have filled R,G, and B channel. So the alpha channel is left and I have two more variables to store. These varibales are both 8 bits. So far I got this : 


half Pack( uint a, uint b ) // a and b both range from 0 to 255
{
    a*=256;//shifting 8 bits
    uint packed = a|b;
    
    ////// How to put this to a half ? 
}

So how can I put the exact bits into the alpha channel ? And then convert the float to uint again to get back a and b values ?


Edited by BlackBrain, 15 January 2014 - 10:30 AM.


Sponsor:

#2 Adam_42   Crossbones+   -  Reputation: 2743

Like
1Likes
Like

Posted 15 January 2014 - 07:03 PM

I think hlsl is missing the function you'd need to do this. If it was 32-bit floats you could just use asfloat() to handle it.

 

I guess you could change the render target to an integer one, and use f32tof16 to encode the float data, but it's not ideal.

 

You could also sacrifice some precision and simply do "return (a * 256) + b;".

 

Any encoding you use would mean you'd need to use point sampling when reading back from the render target as any texture filtering would mess up the encoding.

 

 

The other option is to use MRT to write extra values out to a second render target, which I suspect would be quicker and easier than encoding them into the existing one. It also avoids the texture filtering issues.



#3 BlackBrain   Members   -  Reputation: 378

Like
0Likes
Like

Posted 16 January 2014 - 08:15 AM

Thanks Adam . Your encoding sounds weired to me . If I want to sacrifice precision are these good encoding and decoding functions ?

half encode(uint a, uint b )
{
Return ( a + (b/1000.0f) );
}

Void decode( out uint a , out uint b , half packed)
{
a = (uint) packed;

b = (packed-a)*1000;
}

I am not using MRT because i think intuitively would be slower that way. I have to sample twice to get Gbuffer data . Am i thinking right ?

Thanks again for your time.


Edited by BlackBrain, 16 January 2014 - 08:57 AM.


#4 Styves   Members   -  Reputation: 1102

Like
1Likes
Like

Posted 16 January 2014 - 12:56 PM

Coming up with this on the fly, but assuming your inputs are both in the range 0-1, you should be able to pack the 2 values by storing one value as integer, and the second as fractional, into your alpha:

float Pack( in float a, in float b )
{
  return floor(a * 255) + b; // stores A as integer, B as fractional
}

And to unpack, you can use:

float Unpack( in float c, inout float a, inout float b )
{
  a = floor(c) / 255; // removes the fractional value and scales back to 0-1
  b = frac(c); // removes the integer value, leaving B (0-1)
}

Alternatively, this might be faster for unpacking:

float Unpack( in float c, inout float a, inout float b )
{
  b = frac(c); // removes the integer value, leaving B (0-1)
  a = (c - b) / 255; // removes the fractional value (B) and scales back to 0-1
}

I haven't tested these at all, but I think they should work. smile.png

 

 

Edit: Just re-read the inital post and noticed you're using values from 0-255 uint, instead of float 0-1. The concept should be the same, you can either scale from 0-255 to 0-1, or you can just invert the scaling I applied (instead of increasing A, decrease B).


Edited by Styves, 16 January 2014 - 01:00 PM.


#5 Adam_42   Crossbones+   -  Reputation: 2743

Like
1Likes
Like

Posted 17 January 2014 - 07:21 PM

The reasons I picked "(a * 256) + b" were:

 

- The biggest finite value you can store in a half float is 65504 which is just over 255*256. 255 * 1000 would end up as infinity.

- You don't want unused values in the range, or you're throwing away more precision than necessary.

 

That leaves two obvious options, which are roughly equally good: "(a * 256) + b" and "(a / 256.0) + b". If you care more about precision than performance you can use the sign bit of the value for extra storage, for example by encoding as ((a * 256) + b) - 32768).

 

 

MRT will be slower if the performance of what you're doing is limited by memory bandwidth. It should be quicker if you're ALU bound as it will avoid the extra encoding and decoding work.

 

I'd suggest implementing the simple option first, you can always come back to it later if your profiling says need to optimize it.



#6 milaoshu1982   Members   -  Reputation: 130

Like
1Likes
Like

Posted 17 January 2014 - 10:33 PM

u can pack as this  (float)((a << 256) / max_short) + ( (float)(b / max_short))






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS