-
Notifications
You must be signed in to change notification settings - Fork 308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
wgsl: array size may be module-scope constant (possibly overridable) #1792
Conversation
wgsl/index.bs
Outdated
array_type_decl | ||
| attribute_list* ARRAY LESS_THAN type_decl (COMMA array_size_expr)? GREATER_THAN | ||
|
||
array_size_expr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constrains the expression by grammar.
I could have used literal_or_ident which is defined in the "Attributes" section, and then constrained with a validation rule. I could go either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dneto0 one thing I don't understand, maybe you can clarify it. So this is supported in SPIR-V, right? If I have a structure with such an array, what would be the offset declarations on the fields after this array? They can't be known without a concrete value for a constant, so how would SPIR-V decorate them?
is this an accessibility issue that needs particular attention from the Accessibility Group (APA) ? a11y-needs-resolution is reserved for accessibility issues where APA expects it to be resolved to their satisfaction before a transition. If this issue is related to accessibility, please use a11y-tracker instead and let the APA Group decides on the importance of the issue. |
Yes, this is supported in SPIR-V directly. SPIR-V allows
And "constant instruction" includes spec constants. Yes, offsets are literals, and members can't overlap. So the offset of the field after such an array must be high enough so that it accommodates any array size that is actually used. |
(In fact SPIR-V allows OpSpecConstantOp, which is basically const_expr, which is #1272 and is a post-MVP item. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dneto0 oh wow, let's talk about it some more (maybe on the call?). I'm feeling concerned about this now.
So you are saying that SPIR-V offsets are literal, and they have to take into account all possible (valid) values for such a specialization constant here. To us, it means that SPIR-V can't be automatically generated from WGSL any more. Even though we can specialize the array size in SPIR-V, we can't possibly know what to put into the offset decorations of any fields that follow.
I suppose the same applies to Metal. The long discussion of #1064 was aimed to produce an MTLLibrary at shader module creation. But it's not possible if any of the array sizes are specialized, if I understand correctly. So having this feature hinders our ability to do useful work at shader module creation (at least on SPIR-V and Metal).
No. We can do the following:
The same issue arises in GLSL without a fundamental problem. Example: #version 450
layout(constant_id = 0) const uint N = 10;
layout(set=0,binding=0) buffer B { float data[N]; uint after; } buf;
void main() {
buf.data[1] = 42;
} Produces this SPIR-V:
In this case the compiler (Glslang) assumed an element count of 10, so the offset of the subsequent field was 40. |
All of these require changes to either the syntax, or the semantics of WGSL. So it's something we should consider in strong relation with this PR. We can't land this without having a solution either available, or as a part of this PR.
I don't understand how this is supposed to work. So if I instantiate this entry point with the value of 11, then what happens? Will the pipeline creation fail? or am I going into UB territory with this? |
Pipeline creation fails. We can keep things simple follow Glslang's lead: for a overridable constant that's used in this way, it must have an initializer, and assumes that value is the element count for the purpose of sizing in host-shareable. Then the pipeline check is "between 1 and the initializer value". |
Agreed. Thanks for the important feedback. |
That sounds good to me, not seeing any obvious issues with it right now. |
This is the GLSL-for-Vulkan rule: https://github.com/KhronosGroup/GLSL/blob/master/extensions/khr/GL_KHR_vulkan_glsl.txt#L1203
So it's not just Glslang making it up. :-) |
Hold on. If the specialization doesn't change any layout... what is the point in specializing the array sizes at all? |
The first use case for this is for sizing arrays in workgroup storage. E.g. one element per invocation in the workgroup. When it says "doesn't change layout" what it means is it doesn't change the offsets. (So choose a big offset to start with). |
I'm currently rebasing this change and incorporating the effect of #1135 |
- rename "array size" in many places to the more specific term "element count" - element count may be an unsigned integer literal, as per gpuweb#1135 - describe array type matching in the positive sense, and as an if-and-only-if set of rules. - update example to show array size with unsigned integer literal. - Reword array layout to make "element stride" a defined term, and separately write out how its value is determined.
30ce338
to
f9b4596
Compare
I've rebased, and added a commit that does:
PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks reasonable, if we want to land it. We still need to get feedback from Apple.
| attribute_list* ARRAY LESS_THAN type_decl (COMMA element_count_expression)? GREATER_THAN | ||
|
||
element_count_expression | ||
: INT_LITERAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be our constexpr thing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That depends on where #1272 goes. We can refactor rules later if it converges that way.
* They have the same element type. | ||
* Their element count specifications match, i.e. any of the following are true: | ||
* They are both runtime-sized. | ||
* They are both fixed-sized with element counts |N1| and |N2| such that one of the following is true: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a particular design choice which I'm not 100% sold on, so I wanted to highlight it.
The example below shows cases where certain types are forced to be different by these rules.
But this also means that we lose some ability to 'substitute equals for equals'.
E.g. before:
let a : array<i32,4> = array<i32,4>(); / / zero-init
after:
let size = 4;
let a : array<i32,size> = array<i32,4>(); / / fails type-check
An alternative formulation would allow size-specifiers to match if they are:
- Case 1: literals or non-overridable module-scope constants with the same value
- Case 2: the name of the same overridable constant
We definitely don't want to allow different overridable constants to match, because they can be overridden to different values. The type check should depend only on the source text.
On the whole, I decided to go with the more restrictive formulation. We can relax the rules later. We can't go in the other direction.
After discussing internally, we are against the ability to make array sizes overridable, for a few reasons:
|
The problem with 2 is that this feature is mainly for workgroup storage where the runtime sized arrays are disallowed. We would be happy to limit this feature to prevent it being used in uniform or storage storage classes if there is a clean way. For 3, I thought Metal allowed an entry point parameter of pointer to local, would that not work to some extent? |
The following is unrepresentable in MSL:
Because the |
Right, Metal has different facilities for dynamic workgroup storage: https://developer.apple.com/documentation/metal/mtlrenderpassdescriptor/2866527-threadgroupmemorylength Coming up with a feature that is specifically for solving this more narrow problem would be great! |
This appears to be for render pass tile memory (and only in the very latest version of Metal). Is there an equivalent for workgroup storage? |
So basically, if this proposal is rejected, we need a way for users to control the size of threadgroup memory.
Edit: fixed |
WGSL meeting minutes 2021-07-27
|
I've split out the dynamically-sized threadgroup memory feature to #2024, so we can discuss paths forward on that in its own place, rather than on this PR. |
WGSL meeting minutes 2021-08-10
|
Calling it out a decision confirmed today:
|
In the interest of preserving past decisions, I'll close this PR and open another that allows an element count to be a module-scope constant, but not overridable. |
This exposes an issue with the ldexp tests that I have documented in gpuweb/cts#1791 Fixes gpuweb#1785
It's still an i32; it was only ever an INT_LITERAL in the first place.
Fixed: #1431