@@ -53,6 +53,31 @@ const DEFAULT_CAPABILITIES: DeviceCapabilities = {
5353
5454let cachedCapabilities : DeviceCapabilities | null = null ;
5555
56+ function createCapabilitiesSnapshot (
57+ overrides : Partial < DeviceCapabilities > = { } ,
58+ ) : DeviceCapabilities {
59+ return {
60+ ...DEFAULT_CAPABILITIES ,
61+ detectedAt : Date . now ( ) ,
62+ ...overrides ,
63+ } ;
64+ }
65+
66+ function safeGetCanvasContext (
67+ canvas : HTMLCanvasElement ,
68+ contextId : 'webgl2' | 'webgl' | 'experimental-webgl' ,
69+ ) : RenderingContext | null {
70+ try {
71+ return canvas . getContext ( contextId ) ;
72+ } catch {
73+ return null ;
74+ }
75+ }
76+
77+ function isCanvasProbeUnavailable ( ) : boolean {
78+ return typeof navigator !== 'undefined' && / \b j s d o m \b / i. test ( navigator . userAgent ) ;
79+ }
80+
5681/**
5782 * Detect WebGL support and versions
5883 */
@@ -61,18 +86,22 @@ function detectWebGLSupport(): { supported: boolean; version: 1 | 2 | null } {
6186 return { supported : false , version : null } ;
6287 }
6388
89+ if ( isCanvasProbeUnavailable ( ) ) {
90+ return { supported : false , version : null } ;
91+ }
92+
6493 const canvas = document . createElement ( 'canvas' ) ;
6594
6695 // Try WebGL 2 first
67- const gl2 = canvas . getContext ( 'webgl2' ) ;
96+ const gl2 = safeGetCanvasContext ( canvas , 'webgl2' ) ;
6897 if ( gl2 ) {
6998 return { supported : true , version : 2 } ;
7099 }
71100
72101 // Fall back to WebGL 1
73102 const gl1 =
74- canvas . getContext ( 'webgl' ) ||
75- ( canvas . getContext ( 'experimental-webgl' ) as WebGLRenderingContext | null ) ;
103+ safeGetCanvasContext ( canvas , 'webgl' ) ||
104+ ( safeGetCanvasContext ( canvas , 'experimental-webgl' ) as WebGLRenderingContext | null ) ;
76105 if ( gl1 ) {
77106 return { supported : true , version : 1 } ;
78107 }
@@ -207,25 +236,30 @@ export function detectDeviceCapabilities(): DeviceCapabilities {
207236 const { supported, version } = detectWebGLSupport ( ) ;
208237
209238 if ( ! supported ) {
210- logger . warn ( 'WebGL not supported, using fallback capabilities' ) ;
211- cachedCapabilities = {
212- ...DEFAULT_CAPABILITIES ,
239+ if ( ! isCanvasProbeUnavailable ( ) ) {
240+ logger . warn ( 'WebGL not supported, using fallback capabilities' ) ;
241+ }
242+ cachedCapabilities = createCapabilitiesSnapshot ( {
213243 supportsWebGL2 : false ,
214244 tier : 'low' ,
215- detectedAt : Date . now ( ) ,
216- } ;
245+ } ) ;
217246 return cachedCapabilities ;
218247 }
219248
220249 // Create temporary canvas for detailed detection
221250 const canvas = document . createElement ( 'canvas' ) ;
222251 const gl =
223- ( canvas . getContext ( 'webgl2' ) as WebGL2RenderingContext | null ) ||
224- ( canvas . getContext ( 'webgl' ) as WebGLRenderingContext | null ) ;
252+ ( safeGetCanvasContext ( canvas , 'webgl2' ) as WebGL2RenderingContext | null ) ||
253+ ( safeGetCanvasContext ( canvas , 'webgl' ) as WebGLRenderingContext | null ) ;
225254
226255 if ( ! gl ) {
227- logger . warn ( 'Could not get WebGL context' ) ;
228- cachedCapabilities = DEFAULT_CAPABILITIES ;
256+ if ( ! isCanvasProbeUnavailable ( ) ) {
257+ logger . warn ( 'Could not get WebGL context' ) ;
258+ }
259+ cachedCapabilities = createCapabilitiesSnapshot ( {
260+ supportsWebGL2 : false ,
261+ tier : 'low' ,
262+ } ) ;
229263 return cachedCapabilities ;
230264 }
231265
@@ -234,7 +268,7 @@ export function detectDeviceCapabilities(): DeviceCapabilities {
234268 const tier = detectDeviceTier ( version , gpuMemory , dpr ) ;
235269 const tierSettings = getTierSettings ( tier ) ;
236270
237- cachedCapabilities = {
271+ cachedCapabilities = createCapabilitiesSnapshot ( {
238272 tier,
239273 supportsWebGL2 : version === 2 ,
240274 estimatedGPUMemoryMB : gpuMemory ,
@@ -245,8 +279,7 @@ export function detectDeviceCapabilities(): DeviceCapabilities {
245279 enablePostProcessing : tierSettings . enablePostProcessing ! ,
246280 maxShadowMapSize : tierSettings . maxShadowMapSize ! ,
247281 prefersReducedMotion : getPrefersReducedMotion ( ) ,
248- detectedAt : Date . now ( ) ,
249- } ;
282+ } ) ;
250283
251284 logger . info ( 'Device capabilities detected:' , {
252285 tier,
@@ -258,7 +291,7 @@ export function detectDeviceCapabilities(): DeviceCapabilities {
258291 return cachedCapabilities ;
259292 } catch ( error ) {
260293 logger . error ( 'Error detecting device capabilities:' , error ) ;
261- cachedCapabilities = DEFAULT_CAPABILITIES ;
294+ cachedCapabilities = createCapabilitiesSnapshot ( ) ;
262295 return cachedCapabilities ;
263296 }
264297}
0 commit comments