@@ -144,12 +144,102 @@ private enum PreparedBrushType : uint
144144 /// </summary>
145145 public bool IsSupported => isSupported ??= ProbeSupport ( ) ;
146146
147- private static bool ProbeSupport ( )
147+ /// <summary>
148+ /// Determines whether WebGPU compute support is available on the current system.
149+ /// </summary>
150+ /// <remarks>
151+ /// This method goes beyond checking adapter and device availability — it also compiles
152+ /// a trivial compute shader and creates a compute pipeline to verify the full compute
153+ /// path works. Some systems report a valid device but crash on pipeline creation due to
154+ /// driver or runtime issues.
155+ /// </remarks>
156+ /// <returns>Returns <see langword="true"/> if WebGPU compute support is available; otherwise, <see langword="false"/>.</returns>
157+ public static bool ProbeSupport ( )
148158 {
149159 try
150160 {
151161 using WebGPURuntime . Lease lease = WebGPURuntime . Acquire ( ) ;
152- return WebGPURuntime . TryGetOrCreateDevice ( out _ , out _ , out _ ) ;
162+ if ( ! WebGPURuntime . TryGetOrCreateDevice ( out Device * device , out _ , out _ ) )
163+ {
164+ return false ;
165+ }
166+
167+ WebGPU api = lease . Api ;
168+
169+ // Compile a trivial compute shader and create a pipeline to verify the
170+ // full compute path works end-to-end. Some drivers/runtimes crash at
171+ // DeviceCreateComputePipeline despite successful device creation.
172+ ReadOnlySpan < byte > probeShader = "@compute @workgroup_size(1) fn cs_main() {}\0 "u8 ;
173+ fixed ( byte * shaderCodePtr = probeShader )
174+ {
175+ ShaderModuleWGSLDescriptor wgslDescriptor = new ( )
176+ {
177+ Chain = new ChainedStruct { SType = SType . ShaderModuleWgslDescriptor } ,
178+ Code = shaderCodePtr
179+ } ;
180+
181+ ShaderModuleDescriptor shaderDescriptor = new ( )
182+ {
183+ NextInChain = ( ChainedStruct * ) & wgslDescriptor
184+ } ;
185+
186+ ShaderModule * shaderModule = api . DeviceCreateShaderModule ( device , in shaderDescriptor ) ;
187+ if ( shaderModule is null )
188+ {
189+ return false ;
190+ }
191+
192+ try
193+ {
194+ ReadOnlySpan < byte > entryPoint = "cs_main\0 "u8 ;
195+ fixed ( byte * entryPointPtr = entryPoint )
196+ {
197+ ProgrammableStageDescriptor computeStage = new ( )
198+ {
199+ Module = shaderModule ,
200+ EntryPoint = entryPointPtr
201+ } ;
202+
203+ PipelineLayoutDescriptor layoutDescriptor = new ( )
204+ {
205+ BindGroupLayoutCount = 0 ,
206+ BindGroupLayouts = null
207+ } ;
208+
209+ PipelineLayout * pipelineLayout = api . DeviceCreatePipelineLayout ( device , in layoutDescriptor ) ;
210+ if ( pipelineLayout is null )
211+ {
212+ return false ;
213+ }
214+
215+ try
216+ {
217+ ComputePipelineDescriptor pipelineDescriptor = new ( )
218+ {
219+ Layout = pipelineLayout ,
220+ Compute = computeStage
221+ } ;
222+
223+ ComputePipeline * pipeline = api . DeviceCreateComputePipeline ( device , in pipelineDescriptor ) ;
224+ if ( pipeline is null )
225+ {
226+ return false ;
227+ }
228+
229+ api . ComputePipelineRelease ( pipeline ) ;
230+ return true ;
231+ }
232+ finally
233+ {
234+ api . PipelineLayoutRelease ( pipelineLayout ) ;
235+ }
236+ }
237+ }
238+ finally
239+ {
240+ api . ShaderModuleRelease ( shaderModule ) ;
241+ }
242+ }
153243 }
154244 catch
155245 {
0 commit comments