Maraneshi / HLSL-ConstantBufferLayoutVisualizer

HLSL Constant Buffer Layout Visualizer
https://maraneshi.github.io/HLSL-ConstantBufferLayoutVisualizer/
Other
11 stars 3 forks source link

Potential error in footnote 6 #1

Open tex3d opened 9 months ago

tex3d commented 9 months ago

First, I have to say, stellar work on this! 🤯

I noticed this footnote, and would like to correct any error on the Wiki page this refers to, but I couldn't identify the error: https://github.com/Maraneshi/HLSL-ConstantBufferLayoutVisualizer/blob/3675f1837049f433f5654f591d243143ca82e634/index.html#L808-L809

and then says "legacy" constant buffer rules apply on top of that, which is not true for this specific rule as demonstrated in the example for Rule #4.

This seems to imply a lack of basis for forcing alignment to 16 for structures in the document for Rule 4.

Under "Legacy" Constant Buffer Basics it says:

  1. Aggregates (arrays and structures) are always 16-byte row-aligned. ...

This point appears to be the basis for Rule 4, and it supersedes the basic type alignment rule when a struct appears in a cbuffer.

I think the basic point about alignment for the structure is that the structure itself does not force alignment of 16. The alignment to 16 is required when the struct is included inside a constant buffer.

Perhaps your interpretation of what's written is different than mine. If so, can you more clearly identify the error you're referring to?

Maraneshi commented 9 months ago

The rules as stated in the first section on that page in the DXC Wiki stipulate that a struct must have a type alignment corresponding to its largest member alignment, which is true for Structured Buffers (and C). The sticking point, which is something I've tried (and possibly failed) to elaborate on in the article is that type alignment always requires the size of the type to be a multiple of its alignment. So a struct { double d; float a; } has a size of 16 and the 4 bytes of padding are actually part of the type itself, it doesn't matter what type follows this struct of whether there is even anything at all, it's completely context-independent and applies directly to the type.

The Wiki page then says that the constant buffer layout rules apply on top of those basic alignment rules. In my interpretation, this is not true in this particular case, because the aforementioned struct actually has a real size of 12 in a constant buffer and if you followed it up with e.g. a float, there would be no padding at all in between that and the struct before it, nor would there be padding within the struct itself.

Essentially, my interpretation is that structs and arrays in constant buffers do not inherently have any type alignment at all. Of course, you could turn this around and say that "type alignment" within constant buffers just works differently than it does everywhere else in that it does not require adjusting the size to a multiple of the alignment, but I chose to say it just isn't really type alignment at all.

I should have perhaps not used the word "error", it's just a bit unclear and I had to actually test these rules to figure it out. Now that I read it again, I think the following part of the cbuffer rules actually does cover what I'm talking about, I may have just not been careful enough when reading:

If the aggregate ends in an element that does not completely fill a row, the remaining space on the last row may be used by the next value.

I may remove the footnote entirely in a future update, thanks for double checking with me.

tex3d commented 9 months ago

Ok, I think I understand now.

Although the prior section doesn't say that the size follows the alignment of the type, it is needed for the basic layout and StructuredBuffer, and therefore could be seen as implied. If you make that (reasonable) assumption, it's inconsistent with the size for use in constant buffers.

my interpretation is that structs and arrays in constant buffers do not inherently have any type alignment at all.

That sounds like a valid interpretation. I think you could say the same for matrix types and even vectors. The only alignment being imposed is a scalar element alignment, plus additional rules around the placement of variable declaration fields in a structure or in the constant buffer.

I might be able to clarify the Wiki page a bit given this feedback, thanks!

tex3d commented 9 months ago

I also think it might be useful to call out that the C size alignment rule you refer to simply does not apply at all to constant buffers. The following statement says that the extra rules "effectively make this detail irrelevant", but I don't think that's strong enough.

In C-like struct packing, the size of a type must always be an integer multiple of its alignment. Otherwise, an array of that type would misalign its elements and a struct containing an overaligned type would have its layout depend on its starting address. Note that HLSL has extra rules for arrays and structs which we will see later and effectively make this detail irrelevant in constant buffers.

There are subtle, but important differences in interpreting things in the following two ways:

  1. that the type size is always aligned, but outside elements may be placed into the padding at the end of a type in certain cases.
  2. that the type size is not aligned, and there are extra rules for type layout and element placement.

That, along with an update to the Wiki page, might help.

Maraneshi commented 9 months ago

I also think it might be useful to call out that the C size alignment rule you refer to simply does not apply at all to constant buffers. The following statement says that the extra rules "effectively make this detail irrelevant", but I don't think that's strong enough.

Right, that paragraph used to be in a footnote in the first draft, before I realized that it's kind of central point to my model of alignment and how it applies to constant buffers. I agree it needs stronger wording, I was hedging a bit too hard there (what if HLSL introduced a type with alignment > size in the future? a bit silly). I've actually been slightly afraid that a compiler dev was going to come in and tell me that my entire model is wrong and not at all how it's implemented 😅. Thank you for the useful discussion. I'll update that sentence and the footnote probably later today.