Skip to content

Remove block attribute#2104

Merged
dneto0 merged 6 commits intogpuweb:mainfrom
dneto0:issue-1534-remove-block-attribute
Nov 30, 2021
Merged

Remove block attribute#2104
dneto0 merged 6 commits intogpuweb:mainfrom
dneto0:issue-1534-remove-block-attribute

Conversation

@dneto0
Copy link
Contributor

@dneto0 dneto0 commented Sep 13, 2021

Base PR: #2103

Replace [[block]] with a buffer-root concept.

Part of #1534
Fixes #1008

@dneto0 dneto0 added the wgsl WebGPU Shading Language Issues label Sep 13, 2021
@dneto0 dneto0 added this to the V1.0 milestone Sep 13, 2021
@dneto0
Copy link
Contributor Author

dneto0 commented Sep 13, 2021

@jrprice has some feedback about this concept that it's weird/unsatisfying.

@github-actions
Copy link
Contributor

Previews, as seen when this build job started (242b728):
WebGPU | IDL
WGSL
Explainer

Copy link
Contributor

@kvark kvark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please split these commits into separate PRs?

wgsl/index.bs Outdated

Note: That is, each member type must be a [=plain type=].

A structure type is a <dfn>buffer-root</dfn> if it is used as the [=store type=]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit strange, it's backwards. It says, the type depends on how it's used.
I guess one could describe type inference in a similar way, so it's not too exceptional

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative would be that each struct definition could potentially expand into two SPIR-V constructs: one block and one non-block. Then it would be fine to use the same struct as both buffer-root and non-buffer-root.

It depends on how much you want to abstract away SPIR-V's "block" concept.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative is to keep [[block]].

Thinking about this more, this change carries a SAaaD hazard (Spooky Action at a Distance): if you have a large module, and you decide to add a uniform binding for a particular struct, you may get an error in potentially a totally different place (where this structure was used before, but now is not valid).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree for the PR as is. If the WGSL struct could expand into two SPIR-V structs I think it would avoid SAaaD?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if SPIR-V has to split the structs, you don't need to define the whole "buffer-root" concept. While in general I'd be very wary against splitting types, the main complexity that comes with it is dealing with all the composite dependent types. In case of [[block]] there are not dependent types by definition, so splitting could be done without much blood lost.

@dneto0
Copy link
Contributor Author

dneto0 commented Sep 14, 2021

Can we please split these commits into separate PRs?

Yeah, this builds on #2103, as preliminary work.

@kvark
Copy link
Contributor

kvark commented Sep 14, 2021

I see now. Was expecting the base PR to be mentioned in the main description, but it wasn't.
Will take another look!

@kainino0x
Copy link
Contributor

[process]
Marking this as draft because it shouldn't land before #2103

@kainino0x kainino0x marked this pull request as draft September 14, 2021 04:56
Copy link
Contributor

@kvark kvark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at #2105, [[block]] could also be a way to distinguish between "array of storage buffers" vs "storage buffer with an array of something".

wgsl/index.bs Outdated

Note: That is, each member type must be a [=plain type=].

A structure type is a <dfn>buffer-root</dfn> if it is used as the [=store type=]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative is to keep [[block]].

Thinking about this more, this change carries a SAaaD hazard (Spooky Action at a Distance): if you have a large module, and you decide to add a uniform binding for a particular struct, you may get an error in potentially a totally different place (where this structure was used before, but now is not valid).

Copy link
Contributor

@jrprice jrprice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TLDR: I'm fine with removing the [[block]] attribute, but I think the "buffer-root" concept is unnecessary.


As @kvark already mentioned, changing the type based on its usage seems backwards.

It feels much more natural to me to build up validation rules the other way, which would be consistent with how we currently describe other validation rules in the spec.
We usually (always?) determine where we can use a struct based on its contents, not the other way around.
e.g.

  • if a struct contains an atomic, it can only be used in the Storage or Workgroup storage classes.
  • if a struct is missing shader IO attributes, it cannot be used as an entry parameter or return type.

I don't see a compelling reason to do things differently here.

There are only three validation rules in this PR that use the term "buffer-root".
One of them is entirely redundant, and the other two could be trivially replaced with language that mentions "dynamically-sized array" instead.
I've added inline comments to highlight them.
If we were to make those changes, I /think/ that we could delete the any mention of "buffer-root" from this PR without losing any information about the semantics or validity of a WGSL module.
This suggests to me this "buffer-root" concept is unnecessary (though I appreciate there's some dependence here on how we would plan to support arrays-of-bindings).

This "buffer-root" concept also means that we're not actually getting rid of [[block]], we're just renaming it and making it implicit.
I don't think that this is an improvement over where we are today, since the author has to do extra work now to figure out where that block attribute is going to be applied.
For example, I agree with the action-at-a-distance concern that was already mentioned.
I don't like the notion that adding or removing a variable can make the definition of a struct invalid.

Finally, it also prevents us making further changes to allow Storage/Uniform buffers to use non-structure store types, which several people (myself included) would like to see.

wgsl/index.bs Outdated
* a matrix type
* an [=atomic type|atomic=] type
* an array type
* a [=structure=] type that is not a [=buffer-root=]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of saying "not a buffer-root", this could say "does not contain a dynamically-sized array".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback!
Done.

@kdashg kdashg changed the title Issue 1534 remove block attribute Remove block attribute Oct 22, 2021
@dneto0 dneto0 force-pushed the issue-1534-remove-block-attribute branch from 242b728 to 22a892b Compare November 12, 2021 22:54
@dneto0
Copy link
Contributor Author

dneto0 commented Nov 12, 2021

The new commit is just a rebase.

@github-actions
Copy link
Contributor

Previews, as seen when this build job started (22a892b):
WebGPU | IDL
WGSL
Explainer

@dneto0 dneto0 force-pushed the issue-1534-remove-block-attribute branch from 22a892b to d8bbd15 Compare November 15, 2021 23:00
@dneto0
Copy link
Contributor Author

dneto0 commented Nov 15, 2021

[process] Marking this as draft because it shouldn't land before #2103

This is now independent of #2103. I still need to revisit #2105 which is meant to build on this PR and be considered together.

@dneto0 dneto0 marked this pull request as ready for review November 15, 2021 23:07
@github-actions
Copy link
Contributor

Previews, as seen when this build job started (d8bbd15):
WebGPU | IDL
WGSL
Explainer

@dneto0
Copy link
Contributor Author

dneto0 commented Nov 16, 2021

This is now independent of #2103. I still need to revisit #2105 which is meant to build on this PR and be considered together.

Oops. I think I left some 2103 material in.

And I think this accidentally allows runtime-sized arrays to appear in private, workgroup storage.

</table>

When specified, an element count expression |N| must:
The first element in an array is at index 0, and each successive element is at the next integer index.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Editorially it seemed to make sense to pull this upward.

[SHORTNAME] defines the following attributes that can be applied to array types:
* [=attribute/stride=]

Restrictions on runtime-sized arrays:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first two restrictions are replaced by uses of "fixed footprint" in the definitions of both array and structure types.

@github-actions
Copy link
Contributor

Previews, as seen when this build job started (6dff330):
WebGPU | IDL
WGSL
Explainer

@github-actions
Copy link
Contributor

Previews, as seen when this build job started (a49f5d7):
WebGPU | IDL
WGSL
Explainer

@github-actions
Copy link
Contributor

Previews, as seen when this build job started (d6ae22b):
WebGPU | IDL
WGSL
Explainer

@kvark
Copy link
Contributor

kvark commented Nov 24, 2021

How is the implementation supposed to behave for this case?

struct Foo {
...
};
var<uniform> foo: Foo;
struct Bar {
  foo: Foo,
};

fn func(f: Foo) {
...
}

My understanding is that SPIR-V backend would have to generate 2 different stuctures for Foo: one with the block attribute, and one without. But then how would func signature look like? Would we also need to generate two different func bodies?

@Kangz
Copy link
Contributor

Kangz commented Nov 24, 2021

Wouldn't the argument to func be passed by value, so that it is using the non-decorated version of Foo?

Vulkan 1.2 spec section 15 "Shader Interface"

This specification describes valid uses for Vulkan of these decorations. Any other use of one of these decorations is invalid, with the exception that, when using SPIR-V versions 1.4 and earlier: Block, BufferBlock, Offset, ArrayStride, and MatrixStride can also decorate types and type members used by variables in the Private and Function storage classes.

It seems that newer version of SPIR-V would disallow using a Block-decorated Foo anywhere but in the interface declaration so you'd need two structs anyway.

@kvark
Copy link
Contributor

kvark commented Nov 24, 2021

Ok, so there is one func instance in the generated code. And then the user does func(foo). Are we expected to go and manually copy over the contents (field by field?) from the decorated structure into non-decorated?

@Kangz
Copy link
Contributor

Kangz commented Nov 24, 2021

I think so. That's what happens in GLSL with this small shader (that's a bit similar up to variables in buffer being injected in the global scope):

#version 460
struct Foo {
  int a;
  int b[4];
};

layout(std430, binding=0) buffer MyFoo {
  Foo foo;
};

void f(Foo a) {
}

void main()
{
    f(foo);
}

Copy link
Contributor

@kvark kvark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'm cautiously optimistic about this. Hoping no surprises during implementation, assuming you thought this through :)

@ben-clayton
Copy link
Contributor

ben-clayton commented Nov 25, 2021

How is the implementation supposed to behave for this case?

My understanding is that SPIR-V backend would have to generate 2 different stuctures for Foo: one with the block attribute, and one without. But then how would func signature look like? Would we also need to generate two different func bodies?

We discussed this internally, and the solution I think Tint is going to use is to wrap the uniform and storage buffers with a single-field block-decorated structure, and adjust any uniform / storage buffer member-accesses to go via that single field. All other uses of the structure are kept as-is.

So:

struct Foo {
  i : i32;
};

[[group(0), binding(0)]] var<uniform> ubo: Foo;
[[group(0), binding(1)]] var<storage, read_write> ssbo: Foo;

struct Bar {
  foo: Foo;
};

fn func(f: Foo) {
  ssbo.i = f.i;
  x := ubo;
}

Would internally translate to:

struct Foo {
  i : i32;
};

[[block]] // For internal / illustrative purposes only.
struct FooWrapper {
  inner : Foo;
};

[[group(0), binding(0)]] var<uniform> ubo: FooWrapper;
[[group(0), binding(1)]] var<storage, read_write> ssbo: FooWrapper;

struct Bar {
  foo: Foo;
};

fn func(f: Foo) {
  ssbo.inner.i = f.i;
  x := ubo.inner;
}

This same transformation would work for non-structure storage and buffer types.

@kvark
Copy link
Contributor

kvark commented Nov 25, 2021

Yes, that makes sense. Thanks @ben-clayton !

@dneto0
Copy link
Contributor Author

dneto0 commented Nov 25, 2021

One thing to remember is if there's a runtime-sized array in the struct, then SPIR-V/Vulkan requires it to be in the outermost struct for an SSBO. But WGSL can't use that struct anywhere else anyway.

Added this issue to the next agenda.

@kvark
Copy link
Contributor

kvark commented Nov 25, 2021

Right, so this transformation doesn't apply to all storage/uniform structures, it excludes the ones with run-time arrays. That seems fine.

Part of gpuweb#1534

Change-Id: I4bd26ca668c93d22c265a1ec99bdeadfdfcdfcd4
- Instead of adding various restrictions about where a runtime-sized array
  can appear, invent a "fixed footprint" concept for types.
  A runtime-sized array is not fixed-footprint, but other plain types
  are, provided they don't recursively contain runtime-sized arrays.

- The only remaining restriction for runtime-sized arrays is that
  no expression may evaluate to a runtime-sized array. This is not
  redundant becuase we need to prevent allowing the Load Rule to kick
  in and cause a load of a runtime-sized array.

- Remove the 'block' token.

- Remove remove the attribute list from the start of the structure
  declaration grammar rule.

- Also fix redundant text segments that crept in during an earlier rebase.
* Remove two paragraphs that are part of gpuweb#2103, the array-stride change.
Describe it as "plain type with fixed footprint".
This excludes atomic types, as already expressed by the entry in the
storage classes table.
@dneto0 dneto0 force-pushed the issue-1534-remove-block-attribute branch from d6ae22b to 49b56fd Compare November 25, 2021 20:43
@dneto0
Copy link
Contributor Author

dneto0 commented Nov 25, 2021

Rebased against main. No content change.

@github-actions
Copy link
Contributor

Previews, as seen when this build job started (49b56fd):
WebGPU | IDL
WGSL
Explainer

dneto0 added a commit to dneto0/gpuweb that referenced this pull request Nov 25, 2021
…or workgroup variable

Builds on gpuweb#2104 because that defines the useful concept of
"fixed-footprint" type.

* Refines "fixed-footprint" to add subcategory of "creation-fixed footprint",
  to mean a plain type whose memory footprint is fixed at shader
  creation time. The "fixed-footprint" remains defined as a plain type
  whose memory footprint is fixed by the pipline-creation time.

* Allow element count on a fixed-size array to be an overridable
  module-scope constant.
  * Later I expect we'll have a proper category and name for pipeline-constexpr
    and we can patch this up at that time.

* Previously "fixed-size array" meant element count was a
  shader-creatio-time constant. Now it allows pipeline-creation
  constant, so revisit all uses of "fixed-size array" and further
  qualify with "with creation-fixed footprint" in the intended places:

  In particular:
   - array element must be fixed-footprint.
   - structure member that is fixed-size array must be fixed-footprint

* Note and provide examples that the store type for a workgroup variable
   may have an element count that is pipeline-overridable.
   It has fixed footprint but not necessarily creation-time fixed footprint.

* Editorially emphasize that type constructors only create constructible
  types.

Fixes: gpuweb#2024
@dneto0
Copy link
Contributor Author

dneto0 commented Nov 30, 2021

Approved in 2021-11-30 meeting

@dneto0 dneto0 merged commit 827eeab into gpuweb:main Nov 30, 2021
github-actions bot added a commit that referenced this pull request Nov 30, 2021
SHA: 827eeab
Reason: push, by @dneto0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actions bot added a commit that referenced this pull request Nov 30, 2021
SHA: 827eeab
Reason: push, by @dneto0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actions bot added a commit that referenced this pull request Nov 30, 2021
SHA: 827eeab
Reason: push, by @dneto0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@kdashg
Copy link
Contributor

kdashg commented Dec 1, 2021

WGSL meeting minutes 2021-11-30
  • DN: All the rules that [[block]] was created to enforce are now enforced in a different way. There is a “fixed footprint” rule: if its memory consumption is determined at pipeline creation time, then it can be used as an array element, and [[block]] is no longer needed. And it’s nice not to have struct required for globals.
  • DM: How are these restrictions enforced now?
  • DN: runtime-sized arrays can only appear in the acceptable places now. So you can’t put a runtime-sized array inside a runtime-sized array.
  • MM: Yes, we should get rid of block. Thumbs up.
  • MM: I don’t think this opens the door for not requiring these buffers to hold structs. We could just permit [[block]] on non-struct things.
  • MM: The benefit we’re hoping for from this change would be how we’re going to get to bindless, sometime. What do we want the user to type to distinguish between a two-dimensional array versus a collection of one-dimensional arrays? I had some suggestions in the PR. We could discuss this after MVP.
  • DN: Internally, when Google was discussing this, we were also concerned with preserving a path to bindless.
  • MM: We don’t need to block this on that path. All the ways forward that I came up with involved removing [[block]], so we can certainly do that at this point.
  • DM: Yes, the path seems pretty clear.
  • ACCEPTED
  • DN: And if it’s broken we’ll fix it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wgsl WebGPU Shading Language Issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Do we need [[block]] annotations?

7 participants