Sign in to follow this  
Juliean

Replacing range of bits on int

Recommended Posts

Hello,

 

so I have an int64 as a "sort key", where I set specific bits to group my render calls into e.g. materials from bit 34 to bit 48. Now I wonder, what was an optimized way to set all bits in this range? Seems my bit-math is still a bit off, I can't think of any of the standard opertations to perform this. I could bitshift every single bit from my material id, and then eigther set or unset the respective bit in the sort-key, but there's gotta be a better way to replace all bits from X to Y in an int64 with the respective bits of another int64. Right?

 

void Model::SetMaterial(unsigned int id)
{
//that does only set the bit, but what about already set ones that should be overwritten?
//plus, it would overide all bits up to #64, but we only want this to be up to #48
m_sortKey |= ((unsigned __int64)(m_pMaterial->m_id) << 34);
}
Edited by Juliean

Share this post


Link to post
Share on other sites

Another item to consider is using bit fields.  Often a bit field clears up a lot of confusion while still maintaining the performance.  There are some down sides of course but they can generally be worked around with a union.  I.e.:

 

union
{
  uint64_t    YouDataType;
  struct
  {
      unsigned LowerBits : 16;
      unsigned HighLow : 16;
      unsigned PaddingInt64Bits : 31;
      unsigned HighBit : 1;
  };
};

 

Just pass things around as the underlying 64 bit type and stash it in temporaries of the union type when manipulating it.  The compilers will do all the bit twiddles for you and generally remove the temporary so you won't see any performance differences.  Of course "generally" is a bad word, so test it on your compiler. :)

Share this post


Link to post
Share on other sites

Ah, thats actually a lot nicer, since it feels more "OOP" than fiddeling around with bit operators (did I already mentioned I didn't like bit artitmetics? No? well I should have... :/ ). Finally get to use unions too, never though how and when to learn them properly haha. As for performance, I'm sure going to profile it, but in comparison to whats going on in my render queue, its probably not such a great deal, even if there was a penalty on this.

 

EDIT: one more question though, how can I safely initialize all bits to 0? Do I have to call each "attribute" of the struct individually, because this left both Unused and Layer untouched:

 

 

//only "clears" material and depth   
 m_sortKey.bits = 0;

        union Key64
        {
          unsigned __int64 bits;
          struct
          {
              unsigned Material : 16;
              unsigned Depth : 32;
              unsigned Unused2 : 14;
              unsigned Layer : 2;
          };
        };
 
Edited by Juliean

Share this post


Link to post
Share on other sites

Ah, thats actually a lot nicer, since it feels more "OOP" than fiddeling around with bit operators (did I already mentioned I didn't like bit artitmetics? No? well I should have... :/ ). Finally get to use unions too, never though how and when to learn them properly haha. As for performance, I'm sure going to profile it, but in comparison to whats going on in my render queue, its probably not such a great deal, even if there was a penalty on this.

 

EDIT: one more question though, how can I safely initialize all bits to 0? Do I have to call each "attribute" of the struct individually, because this left both Unused and Layer untouched:

 

 

//only "clears" material and depth   
 m_sortKey.bits = 0;

        union Key64
        {
          unsigned __int64 bits;
          struct
          {
              unsigned Material : 16;
              unsigned Depth : 32;
              unsigned Unused2 : 14;
              unsigned Layer : 2;
          };
        };
 

Simple:

 

Key64 temp;

temp.bits = 0;

 

Assumption though, your bit field does not exceed the size of the int64, which in this case is true.

Share this post


Link to post
Share on other sites

Simple:



Key64 temp;

temp.bits = 0;



Assumption though, your bit field does not exceed the size of the int64, which in this case is true.

 

Thats just how I did it (see first two liens of sample code, maybe should have declared that variable here first), it seems that "not clearing" of unused and layer was just an display error of some sort, "bits" indeed was 0.

Share this post


Link to post
Share on other sites

Simple:

Key64 temp;

temp.bits = 0;

Assumption though, your bit field does not exceed the size of the int64, which in this case is true.

 

Thats just how I did it (see first two liens of sample code, maybe should have declared that variable here first), it seems that "not clearing" of unused and layer was just an display error of some sort, "bits" indeed was 0.

Just for safety, you might want to do a quick assertion to verify things:

 

assert( sizeof( Key64 )==sizeof( __int64 ) );

 

Looking at things, this should be true but double checking is always a good idea.

 

 

Oops, just noticed a gotcha.  You are trying to pack the 32 bit depth at a midpoint of your fields it is likely getting packed in the next 32bit value.  So your bitfield is likely going to be 3 32 bit items instead of the pair you were hoping for.  Gotta double check this, but pretty sure that is true.

 

Double checked: this is likely going to be the problem.  The underlying type is not large enough for the 32 bit item so it packs into the next full 32 bit item.  If you change to an 'unsigned long long' it should go back to what you desire.  (C99 only allows up to int/unsigned int, C++ allows any integral type to be used as the underlying type.)

 

Couple edits later, think I have this all correct now. smile.png

 

union Key64
        {
          unsigned __int64 bits;
          struct
          {
              unsigned long long Material : 16;
              unsigned long long Depth : 32;

              unsigned long long Unused2 : 14;

              unsigned long long Layer : 2;

          };
        };

 

Should fix the problem and also be valid C++.

Edited by AllEightUp

Share this post


Link to post
Share on other sites

Couple edits later, think I have this all correct now. smile.png



union Key64
{
unsigned __int64 bits;
struct
{
unsigned long long Material : 16;
unsigned long long Depth : 32;

unsigned long long Unused2 : 14;

unsigned long long Layer : 2;

};
};



Should fix the problem and also be valid C++.

 

Okay, thanks, would have never quessed that! But isn't this going to mean that the union will have 4 times long long size, or is it just some sort of hint to the compiler how he should pack those bits?

Share this post


Link to post
Share on other sites

Should fix the problem and also be valid C++.

 

Okay, thanks, would have never quessed that! But isn't this going to mean that the union will have 4 times long long size, or is it just some sort of hint to the compiler how he should pack those bits?

Nope, it should turn out the same size as the uint64 you want.  Basically the bit field will steal bits from the underlying type till it runs out.  So it takes 16, then 32, then 14 and then 2, which means it never ran out of bits so it just uses the single uint64.  Throw that assert in I mentioned and it should verify the behavior for you.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this