@@ -60,9 +60,97 @@ export function quitIfFeaturesNotAvailable(
6060 }
6161}
6262
63+ function supportsDirectBufferBinding ( device : GPUDevice ) : boolean {
64+ const buffer = device . createBuffer ( {
65+ size : 16 ,
66+ usage : GPUBufferUsage . UNIFORM ,
67+ } ) ;
68+ const layout = device . createBindGroupLayout ( {
69+ entries : [ { binding : 0 , visibility : GPUShaderStage . FRAGMENT , buffer : { } } ] ,
70+ } ) ;
71+
72+ try {
73+ device . createBindGroup ( {
74+ layout,
75+ entries : [ { binding : 0 , resource : buffer } ] ,
76+ } ) ;
77+ return true ;
78+ } catch {
79+ return false ;
80+ } finally {
81+ buffer . destroy ( ) ;
82+ }
83+ }
84+
85+ function supportsDirectTextureBinding ( device : GPUDevice ) : boolean {
86+ const texture = device . createTexture ( {
87+ size : [ 1 ] ,
88+ usage : GPUTextureUsage . TEXTURE_BINDING ,
89+ format : 'rgba8unorm' ,
90+ } ) ;
91+ const layout = device . createBindGroupLayout ( {
92+ entries : [ { binding : 0 , visibility : GPUShaderStage . FRAGMENT , texture : { } } ] ,
93+ } ) ;
94+
95+ try {
96+ device . createBindGroup ( {
97+ layout,
98+ entries : [ { binding : 0 , resource : texture } ] ,
99+ } ) ;
100+ return true ;
101+ } catch {
102+ return false ;
103+ } finally {
104+ texture . destroy ( ) ;
105+ }
106+ }
107+
108+ function supportsDirectTextureAttachments ( device : GPUDevice ) : boolean {
109+ const texture = device . createTexture ( {
110+ size : [ 1 ] ,
111+ usage : GPUTextureUsage . RENDER_ATTACHMENT ,
112+ format : 'rgba8unorm' ,
113+ sampleCount : 4 ,
114+ } ) ;
115+ const resolveTarget = device . createTexture ( {
116+ size : [ 1 ] ,
117+ usage : GPUTextureUsage . RENDER_ATTACHMENT ,
118+ format : 'rgba8unorm' ,
119+ } ) ;
120+ const depthTexture = device . createTexture ( {
121+ size : [ 1 ] ,
122+ usage : GPUTextureUsage . RENDER_ATTACHMENT ,
123+ format : 'depth16unorm' ,
124+ sampleCount : 4 ,
125+ } ) ;
126+ const encoder = device . createCommandEncoder ( ) ;
127+ try {
128+ const pass = encoder . beginRenderPass ( {
129+ colorAttachments : [
130+ { view : texture , resolveTarget, loadOp : 'load' , storeOp : 'store' } ,
131+ ] ,
132+ depthStencilAttachment : {
133+ view : depthTexture ,
134+ depthLoadOp : 'load' ,
135+ depthStoreOp : 'store' ,
136+ } ,
137+ } ) ;
138+ pass . end ( ) ;
139+ return true ;
140+ } catch ( e ) {
141+ console . error ( e ) ;
142+ return false ;
143+ } finally {
144+ encoder . finish ( ) ;
145+ texture . destroy ( ) ;
146+ resolveTarget . destroy ( ) ;
147+ }
148+ }
149+
63150/**
64151 * Shows an error dialog if getting a adapter or device wasn't successful,
65- * or if/when the device is lost or has an uncaptured error.
152+ * or if/when the device is lost or has an uncaptured error. Also checks
153+ * for direct buffer binding, direct texture binding, and direct texture attachment binding.
66154 */
67155export function quitIfWebGPUNotAvailable (
68156 adapter : GPUAdapter | null ,
@@ -80,6 +168,16 @@ export function quitIfWebGPUNotAvailable(
80168 device . addEventListener ( 'uncapturederror' , ( ev ) => {
81169 fail ( `Uncaptured error:\n${ ev . error . message } ` ) ;
82170 } ) ;
171+
172+ if (
173+ ! supportsDirectBufferBinding ( device ) ||
174+ ! supportsDirectTextureBinding ( device ) ||
175+ ! supportsDirectTextureAttachments ( device )
176+ ) {
177+ fail (
178+ 'Core features of WebGPU are unavailable. Please update your browser to a newer version.'
179+ ) ;
180+ }
83181}
84182
85183/** Fail by showing a console error, and dialog box if possible. */
0 commit comments