Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

greggman
Copy link
Contributor

Effectively, in both compat and core, requesting limits for maxStorageBuffersInFragmentStage and/or maxStorageBuffersInVertexStage that are greater than maxStorageBuffersPerShaderStage is an error.

Similarly, in both compat and core, requesting limit for maxStorageTexturesInFragmentStage and/or maxStorageTexturesInVertexStage that are greater than maxStorageTexturesPerShaderStage is an error.

Assume the adapter supports 16 of each:

requiredLimits: {
  maxStorageBuffersInFragmentStage: 3,
}

In compat this return a device with

limits: {
  maxStorageBuffersInFragmentStage: 3,
  maxStorageBuffersPerShaderStage: 4,   // the default in compat
}

In core it returns a device with

limits: {
  maxStorageBuffersInFragmentStage: 8,
  maxStorageBuffersPerShaderStage: 8,   // the default in core
}

Another example:

requiredLimits: {
  maxStorageBuffersInFragmentStage: 9,
  maxStorageBuffersPerShaderStage: 10,
}

In compat this return a device with

limits: {
  maxStorageBuffersInFragmentStage: 9,
  maxStorageBuffersPerShaderStage: 10,
}

In core it returns a device with

limits: {
  maxStorageBuffersInFragmentStage: 10,
  maxStorageBuffersPerShaderStage: 10,
}

This reflects what is actually enforced. Another option is to have core report the same as compat even though it would not enforce the maxStorageXXXInXXXStage limits.

See the proposal:

Effectively, in both compat and core, requesting limits for
`maxStorageBuffersInFragmentStage` and/or `maxStorageBuffersInVertexStage`
that are greater than `maxStorageBuffersPerShaderStage` is an error.

Similarly, in both compat and core, requesting limit for
`maxStorageTexturesInFragmentStage` and/or `maxStorageTexturesInVertexStage`
that are greater than `maxStorageTexturesPerShaderStage` is an error.

Assume the adapter supports 16 of each:

```js
requiredLimits: {
  maxStorageBuffersInFragmentStage: 3,
}
```

In compat this return a device with

```js
limits: {
  maxStorageBuffersInFragmentStage: 3,
  maxStorageBuffersPerShaderStage: 4,
}
```

In core it returns a device with

```js
limits: {
  maxStorageBuffersInFragmentStage: 4,
  maxStorageBuffersPerShaderStage: 4,
}
```

Another example:

```js
requiredLimits: {
  maxStorageBuffersInFragmentStage: 9,
  maxStorageBuffersPerShaderStage: 10,
}
```

In compat this return a device with

```js
limits: {
  maxStorageBuffersInFragmentStage: 9,
  maxStorageBuffersPerShaderStage: 10,
}
```

In core it returns a device with

```js
limits: {
  maxStorageBuffersInFragmentStage: 10,
  maxStorageBuffersPerShaderStage: 10,
}
```
Copy link
Contributor

github-actions bot commented Dec 23, 2024

Previews, as seen when this build job started (241f25f):
WebGPU webgpu.idl | Explainer | Correspondence Reference
WGSL grammar.js | wgsl.lalr.txt

@greggman
Copy link
Contributor Author

greggman commented Jan 3, 2025

To add to this, maybe another way talk about it is to list what happens in each situation

  • What's valid for adapter.limits in compat and core
  • What's valid for requestDevice({requiredLimits} in compat and core
  • What's valid for device.limits in compat and core.
  • What is validated on usage (createBindGroupLayout etc...)

What's valid for adapter.limits in compat and core

  • In compat, maxXXXInStage must be less than or equal to maxXXXPerStage
  • In core, maxXXXInStage must be equal to maxXXXPerStage

What's valid for requestDevice({requiredLimits} in compat and core

  • In compat, maxXXXInStage must be less than or equal to maxXXXPerStage
  • In core ??? same validation? Or is maxXXXInStage ignored?

What's valid for device.limits in compat and core.

  • In compat, maxXXXInStage matches successful request
  • In core, maxXXXInStage equals maxXXXPerStage

What is validated on usage (createBindGroupLayout etc...)

  • In compat (as specified in proposal)
  • In core (as it is now, maxXXXInStage limits are ignored)

@kainino0x
Copy link
Contributor

What's valid for requestDevice({requiredLimits} in compat and core

  • In compat, maxXXXInStage must be less than or equal to maxXXXPerStage
  • In core ??? same validation? Or is maxXXXInStage ignored?

Same validation makes sense to me, it's a bit less surprising.

In both compat and core there's a theoretical possibility of someone doing something like
{ requiredLimits: { ...adapter.limits, maxXXXPerStage: 8 } }
(pretend that does what it looks like even though it does not work today)
which would then break, but this probably is not realistic.

What is validated on usage (createBindGroupLayout etc...)

  • In compat (as specified in proposal)
  • In core (as it is now, maxXXXInStage limits are ignored)

We don't actually have to write in the spec that they're ignored, right? device.limits gets normalized so that they're equal, so the InStage limits have no effect.

Copy link
Contributor

@kainino0x kainino0x left a comment

Choose a reason for hiding this comment

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

LGTM so far, might add some of the details you posted in comments though

@greggman
Copy link
Contributor Author

Sigh.... this topic is being discussed in about 9 places. I wrote a bunch of comments in one of them but I can't find them today. Maybe I forgot to submit. I know Kai wrote a comment I was responding to but I can't find that one either. Mine was about suggesting maxXXXPerShaderStage default 0 in compat (different idea below)

Places I checked #5033 #5013 #5029 #5037 #5036 #5018

In any case, trying to update the CTS has pointed out some issues. Namely that you can write code that runs locally but fails elsewhere. Since maxXXXPerStage defaults to 4 the if you write

requiredLimits: {
  maxXXXInShaderStage: adapter.limits.maxXXXInShaderStage,
}

If the value is less <= 4 you don't get an error so you think your code works but it doesn't.

It occured to me last night, maybe instead of it being a validation error, if the requested maxXXXInYYYShaderStage is > maxXXXPerShaderStage, we should just auto-upgrade maxXXXPerShaderStage. This would happen in both core and compat.

wdyt?

@kainino0x kainino0x added the compat WebGPU Compatibility Mode label Jan 13, 2025
@kainino0x
Copy link
Contributor

Great point, seems like a validation error won't work. Without a validation error, if you ask for PerStage < InFragmentStage then I think there's 3 other possibilities:

  1. validation error
  2. don't reify, just apply both limits as requested
  3. lower InFragmentStage down to PerStage
  4. raise PerStage up to InFragmentStage, what you suggested

1 and 2 have the same result except for whether device.limits gets normalized or not.
Both 1/2 and 3 have slightly surprising behaviors but they're both reasonable... off the top of my head, haven't thought of a good reason to pick one over the other, but you might remember/realize a problem with 1/2.

@greggman
Copy link
Contributor Author

greggman commented Jan 14, 2025

  1. don't reify, just apply both limits as requested

    applying both limits as requested isn't compatible with existing core. Well, I mean, the new limits don't exist in core (yet) but the idea is every compat app is valid core and core effectively says the only limit is maxXXXPerStage.

  2. lower InFragmentStage down to PerStage

    lowering a limit lower than requested seems problematic and every different from the core design. Unless I'm mis-understanding this proposal

  3. raise PerStage up to InFragmentStage.

    I think only 3 really works?

@kainino0x
Copy link
Contributor

1. applying both limits as requested isn't compatible with existing core. Well, I mean, the new limits don't exist in core (yet) but the idea is every compat app is valid core and core effectively says the only limit is maxXXXPerStage.

Ah right, it would require a carveout that says core just ignores those. Not sure if there are any other problems. Fine with me to not consider this further.

2. lowering a limit lower than requested seems problematic and every different from the core design. Unless I'm mis-understanding this proposal

It depends on whether you consider the limit by itself (which yes will be lower than requested) or the limits as a whole. It wouldn't change the enforced limit (which is still min(requiredLimits.*In*Stage, requiredLimits.*PerStage)), so device.limits would just be telling you what the effective *In*Stage limit is.

@greggman
Copy link
Contributor Author

  1. lowering a limit lower than requested seems problematic and every different from the core design. Unless I'm mis-understanding this proposal

It depends on whether you consider the limit by itself (which yes will be lower than requested) or the limits as a whole. It wouldn't change the enforced limit (which is still min(requiredLimits.*In*Stage, requiredLimits.*PerStage)), so device.limits would just be telling you what the effective *In*Stage limit is.

Maybe I'm misunderstanding but, it feels like this code is valid

const numNeeded = 6;
if (adapter.limits.maxSomeLimit < numNeeded) {
   // tell user they can't run my app
}
const device = adapter.requestDevice({ 
   requiredLimits: { maxSomeLimit: numNeed },
});

// use numNeeded

My understanding of (2) is that, no, this code won't work. Because device.limits.maxSomeNeeded might get lowered to lower than you requested. That seems like a big change.

Did I misunderstand (2)?

@greggman
Copy link
Contributor Author

greggman commented Jan 22, 2025

I think better in code so here's the proposal in code

   requestDevice({ requestedLimits }) {
     // make a copy of the requested limits
     const effectiveRequestedLimits = { ...requestedLimits };

     // update the effective requested limits
     effectiveRequestedLimits.maxStorageBuffersPerShaderStage = Math.max(
        requestedLimits.maxStorageBuffersInFragmentStage,
        requestedLimits.maxStorageBuffersInVertexStage,
        requestedLimits.maxStorageBuffersPerShaderStage);

     effectiveRequestedLimits.maxStorageTexturesPerShaderStage = Math.max(
        requestedLimits.maxStorageTexturesInFragmentStage,
        requestedLimits.maxStorageTexturesInVertexStage,
        requestedLimits.maxStorageTexturesPerShaderStage);

     // request the device using the effectiveRequestedLimits following the normal
     // validation rules. If any requested limit exceeds the adapter's limit then
     // throw an OperationError

     // device creation succeeded

     if (featureLevel === 'core') {
       // this effectively leaves core validation the same as it has always been.
       device.limits.maxStorageBuffersInFragmentStage = device.limit.maxStorageBuffersPerShaderStage;
       device.limits.maxStorageBuffersInVertexStage = device.limit.maxStorageBufferPerShaderStage;

       device.limits.maxStorageTexturesInFragmentStage = device.limit.maxStorageTexturesPerShaderStage;
       device.limits.maxStorageTexturesInVertexStage = device.limit.maxStorageTexturesPerShaderStage;
     }

With the limits set as above, apply the validation as specified in the proposal at createBindGroupLayout, createPipelineLayout (including "auto" layout creation)

@Kangz
Copy link
Contributor

Kangz commented Jan 27, 2025

GPU Web WG 2025-01-22 Atlantic-time
  • In particular maxXXXInYYStage auto-upgrades maxXXXPerShaderStage in both core and compat. (visa-versa is already true in core)
  • GT: Already agree but the other update was it’s a bindGroupLayout
  • Resolved: Approved.

Copy link
Contributor

@kainino0x kainino0x left a comment

Choose a reason for hiding this comment

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

small nits, LGTM

@kainino0x kainino0x merged commit f3ffc03 into gpuweb:main Jan 27, 2025
3 of 4 checks passed
@greggman greggman deleted the maxSTB-tweak branch January 30, 2025 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compat WebGPU Compatibility Mode
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants