-
Notifications
You must be signed in to change notification settings - Fork 352
Compat-ify samples (more difficult). #495
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
Conversation
48ef20c to
a6c3e5f
Compare
These samples required changes beyond requesting compatibility mode. a-buffer * requires storage buffers in fragment stage (issue 19) * depth texture binding cannot be used in textureLoad() (issue 16) * bind as texture_2d<f32> (and unfilterable-float sampleType) instead * interpolate(flat) not supported (issue 17) * use interpolate(flat, either) instead bitonicSort * requires storage buffers in fragment stage (issue 19) cubemap * textureBindingViewDimension must be specified for cubemap sampling (issue 1) deferredRendering * depth texture binding cannot be used in textureLoad() (issue 16) * bind as texture_2d<f32> (and unfilterable-float sampleType) instead * requires storage buffers in fragment stage (issue 19) reversedZ * depth texture binding cannot be used in textureLoad() (issue 16) * bind as texture_2d<f32> (and unfilterable-float sampleType) instead samplerParameters * requires storage buffers in vertex stage (issue 18) skinnedMesh * requires storage buffers in vertex stage (issue 18) textRenderingMsdf * requires storage buffers in vertex stage (issue 18) * requires storage buffers in fragment stage (issue 19) videoUploading * no additional changes required (missed this one in the first pass) wireframe * requires storage buffers in vertex stage (issue 18)
a6c3e5f to
a5044b8
Compare
greggman
left a comment
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.
See comments. The pattern in the PR would be okay in say 6 months but it doesn't work today.
| const adapter = await navigator.gpu?.requestAdapter({ | ||
| featureLevel: 'compatibility', | ||
| }); | ||
| const device = await adapter?.requestDevice({ | ||
| requiredLimits: { maxStorageBuffersInFragmentStage: 2 }, | ||
| }); |
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.
@kainino0x pointed out this will fail on current chrome/firefox/safari
To make it work requires only adding it IF maxStorageBuffersInFragmentStage exists on adapter.limits because otherwise it's an unknown limit and will fail device creation.
There's a bunch of solutions - they all have trade offs.
Here's one
| const adapter = await navigator.gpu?.requestAdapter({ | |
| featureLevel: 'compatibility', | |
| }); | |
| const device = await adapter?.requestDevice({ | |
| requiredLimits: { maxStorageBuffersInFragmentStage: 2 }, | |
| }); | |
| const adapter = await navigator.gpu?.requestAdapter({ | |
| featureLevel: 'compatibility', | |
| }); | |
| const { maxStorageBuffersInFragmentStage } = adapter.limits; | |
| // Note: maxStorageBuffersInFragmentStage may be undefined in old browsers | |
| // but that's ok as it will fail this check and old browser only support core | |
| // which always has enough storage buffers in fragment shaders. | |
| if (maxStorageBuffersInFragmentStage < 2) { | |
| fail('maxStorageBuffersInFragmentStage < 2'); // export from util.ts | |
| } | |
| const device = await adapter?.requestDevice({ | |
| requiredLimits: { maxStorageBuffersInFragmentStage }, | |
| ); |
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.
Note: I'm not sure that example is acceptable nor not since it's not requesting 2, it's requesting max. The more I have to deal with features and limits the more think you should just always get max limits. It's much easier to deal with.
const adapter = await navigator.gpu?.requestAdapter({
featureLevel: 'compatibility',
});
const device = await adapter.requestDevice({
requiredFeatures: adapter.features,
requiredLimits: objLikeToObj(adapter.limits), // need a helper ATM
});
if (device.limits.maxStorageBuffersInFragmentStage < 2) {
fail('I can has no running');
}It's way simpler than the summersault of checking for in adapter and applying in device but it's not the intent of the spec. 😕
function objLikeToObj(objLike) {
const obj = {};
for (const key of objLike) {
obj[key] = objLike[key];
}
return obj;
}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.
Thanks.
const { maxStorageBuffersInFragmentStage } = adapter.limits;
gave me:
(!) [plugin typescript] sample/a-buffer/main.ts (20:9): @rollup/plugin-typescript TS2339: Property 'maxStorageBuffersInFragmentStage' does not exist on type 'GPUSupportedLimits'.
/home/senorblanco/src/webgpu-samples/sample/a-buffer/main.ts:20:9
I used
if ('maxStorageBuffersInFragmentStage' in adapter.limits) {
instead. LMK if that's kosher.
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.
Note: I'm not sure that example is acceptable nor not since it's not requesting 2, it's requesting max. The more I have to deal with features and limits the more think you should just always get max limits. It's much easier to deal with.
It is easier, but I think it's dangerous, in that it turns WebGPU into OpenGL: you could write some code that runs fine on your machine (with above-average limits), but won't run elsewhere (with default limits). I'd prefer to request the minimum that the sample actually needs.
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.
const { maxStorageBuffersInFragmentStage } = adapter.limits;
It gets an error because the types need to be updated
npm install --save-dev @webgpu/types@latest
This will prevent breakage on WebGPU implementations that don't support those limits yet.
0fedee7 to
02db960
Compare
| if ('maxStorageBuffersInFragmentStage' in adapter.limits) { | ||
| limits = { maxStorageBuffersInFragmentStage: 2 }; | ||
| } | ||
| const device = await adapter?.requestDevice({ |
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 code will just throw if maxStorageBuffersInFragmentStage < 2 and the user gets a blank screen. Given we surface other errors I think it would be good to surface something, which is why I suggested something like
if (maxStorageBuffersInFragmentStage < 2) {
fail('maxStorageBuffersInFragmentStage < 2); // export from util.ts
}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.
Good catch, thanks. I thought the samples were checking for requestDevice() failure, but I see they're not.
I've implemented better limit checking. LMK what you think.
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.
requestDevice never fails (never turns null/undefined) so "if called correctly" there's nothing to check
fbba277 to
378e878
Compare
greggman
left a comment
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.
lgtm! one nit.
| if (limit in adapter.limits) { | ||
| const limitKey = limit as keyof GPUSupportedLimits; | ||
| const limitValue = adapter.limits[limitKey] as number; | ||
| if (limitValue < requiredValue) { |
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.
nit: some limits are minXXX instead of maxXXX. Up to you if you want if it now or wait for someone to stumble on it.
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.
Renamed QuitIfLimitNotMet() -> QuitIfLimitLessThan(). Someone else can make the GreaterThan flavour if desired.
2c2744e to
c9770d4
Compare
Implement a "quitIfLimitNotMet()" helper that tests for the existence of a limit in adapter.limits, checks that it meets a certain value, and errors if not. If successful, it will add the limit to a GPURequiredLimits object.
c9770d4 to
85eb241
Compare
These samples required changes beyond requesting compatibility mode.
a-buffer
textureLoad()(issue 16)texture_2d<f32>(andunfilterable-floatsampleType) insteadinterpolate(flat)not supported (issue 17)interpolate(flat, either)insteadbitonicSort
cubemap
textureBindingViewDimensionmust be specified for cubemap sampling (issue 1)deferredRendering
textureLoad()(issue 16)texture_2d<f32>(andunfilterable-floatsampleType) insteadreversedZ
textureLoad()(issue 16)texture_2d<f32>(andunfilterable-floatsampleType) insteadsamplerParameters
skinnedMesh
textRenderingMsdf
videoUploading
wireframe