From 7fa646e2d9a67a868bbce0601fd5b923d656f1e2 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 31 Dec 2025 13:35:07 -0600 Subject: [PATCH] Implement robust WebGL fallback for 3D rendering Add automatic fallback from hardware to software WebGL rendering with graceful degradation when WebGL is completely unavailable. Changes: - Add tryCreateWebGLContext() helper with 3-tier fallback - Try hardware WebGL, then software WebGL, then graceful disable - Show user-friendly messages when 3D unavailable - Provide no-op functions to prevent crashes - Add console logging for WebGL rendering method Fixes crashes when WebGL unavailable in Setup and Magnetometer tabs. Improves UX for users with GPU driver issues or running in VMs. Files modified: - tabs/setup.js: Robust WebGL detection - tabs/magnetometer.js: Robust WebGL detection --- tabs/magnetometer.js | 85 +++++++++++++++++++++++++++++++++++++++----- tabs/setup.js | 84 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 154 insertions(+), 15 deletions(-) diff --git a/tabs/magnetometer.js b/tabs/magnetometer.js index 314f29fe2..7951fb91e 100644 --- a/tabs/magnetometer.js +++ b/tabs/magnetometer.js @@ -630,15 +630,84 @@ TABS.magnetometer.initialize3D = function () { canvas = $('.model-and-info #canvas'); wrapper = $('.model-and-info #canvas_wrapper'); - // webgl capability detector - // it would seem the webgl "enabling" through advanced settings will be ignored in the future - // and webgl will be supported if gpu supports it by default (canary 40.0.2175.0), keep an eye on this one - var detector_canvas = document.createElement('canvas'); - if (window.WebGLRenderingContext && (detector_canvas.getContext('webgl') || detector_canvas.getContext('experimental-webgl'))) { - renderer = new THREE.WebGLRenderer({canvas: canvas.get(0), alpha: true, antialias: true}); - useWebGlRenderer = true; + // Robust WebGL capability detection with fallback + function tryCreateWebGLContext() { + if (!window.WebGLRenderingContext) { + return null; + } + + const detector_canvas = document.createElement('canvas'); + let gl = null; + let renderMethod = null; + + // Try 1: Hardware-accelerated WebGL (best performance) + try { + gl = detector_canvas.getContext('webgl') || detector_canvas.getContext('experimental-webgl'); + if (gl) { + renderMethod = 'hardware'; + console.log('[3D Magnetometer] Using hardware-accelerated WebGL'); + } + } catch (e) { + console.warn('[3D Magnetometer] Hardware WebGL failed:', e); + } + + // Try 2: Software-rendered WebGL (slower but more compatible) + if (!gl) { + try { + gl = detector_canvas.getContext('webgl', { failIfMajorPerformanceCaveat: false }) || + detector_canvas.getContext('experimental-webgl', { failIfMajorPerformanceCaveat: false }); + if (gl) { + renderMethod = 'software'; + console.log('[3D Magnetometer] Using software-rendered WebGL (slower performance)'); + } + } catch (e) { + console.warn('[3D Magnetometer] Software WebGL failed:', e); + } + } + + return gl ? { context: gl, method: renderMethod } : null; } - + + const webglResult = tryCreateWebGLContext(); + + if (webglResult) { + try { + renderer = new THREE.WebGLRenderer({canvas: canvas.get(0), alpha: true, antialias: true}); + useWebGlRenderer = true; + + // Show performance notice if using software rendering + if (webglResult.method === 'software') { + GUI_control.prototype.log('3D view using software rendering (slower). Consider updating graphics drivers or disabling hardware acceleration in Options.'); + } + } catch (e) { + console.error('[3D Magnetometer] Failed to create THREE.WebGLRenderer:', e); + renderer = null; + useWebGlRenderer = false; + } + } + + // Check if WebGL is available + if (!renderer) { + // WebGL not supported - show fallback message + wrapper.html('
' + + '
' + + '

3D view unavailable

' + + '

WebGL could not be initialized. This may be due to:

' + + '
    ' + + '
  • Graphics drivers need updating
  • ' + + '
  • Hardware acceleration issues
  • ' + + '
  • Browser or system limitations
  • ' + + '
' + + '

Try: Options → Disable 3D Hardware Acceleration, then restart

' + + '
' + + '
'); + + // Provide no-op functions so the rest of the tab doesn't break + this.render3D = function () {}; + this.resize3D = function () {}; + return; + } + // initialize render size for current canvas size renderer.setSize(wrapper.width() * 2, wrapper.height() * 2); diff --git a/tabs/setup.js b/tabs/setup.js index cb05fee3c..4f112bd8f 100755 --- a/tabs/setup.js +++ b/tabs/setup.js @@ -225,14 +225,84 @@ TABS.setup.initialize3D = function () { canvas = $('.model-and-info #canvas'); wrapper = $('.model-and-info #canvas_wrapper'); - // webgl capability detector - // it would seem the webgl "enabling" through advanced settings will be ignored in the future - // and webgl will be supported if gpu supports it by default (canary 40.0.2175.0), keep an eye on this one - var detector_canvas = document.createElement('canvas'); - if (window.WebGLRenderingContext && (detector_canvas.getContext('webgl') || detector_canvas.getContext('experimental-webgl'))) { - renderer = new THREE.WebGLRenderer({canvas: canvas.get(0), alpha: true, antialias: true}); - useWebGlRenderer = true; + // Robust WebGL capability detection with fallback + function tryCreateWebGLContext() { + if (!window.WebGLRenderingContext) { + return null; + } + + const detector_canvas = document.createElement('canvas'); + let gl = null; + let renderMethod = null; + + // Try 1: Hardware-accelerated WebGL (best performance) + try { + gl = detector_canvas.getContext('webgl') || detector_canvas.getContext('experimental-webgl'); + if (gl) { + renderMethod = 'hardware'; + console.log('[3D] Using hardware-accelerated WebGL'); + } + } catch (e) { + console.warn('[3D] Hardware WebGL failed:', e); + } + + // Try 2: Software-rendered WebGL (slower but more compatible) + if (!gl) { + try { + gl = detector_canvas.getContext('webgl', { failIfMajorPerformanceCaveat: false }) || + detector_canvas.getContext('experimental-webgl', { failIfMajorPerformanceCaveat: false }); + if (gl) { + renderMethod = 'software'; + console.log('[3D] Using software-rendered WebGL (slower performance)'); + } + } catch (e) { + console.warn('[3D] Software WebGL failed:', e); + } + } + + return gl ? { context: gl, method: renderMethod } : null; } + + const webglResult = tryCreateWebGLContext(); + + if (webglResult) { + try { + renderer = new THREE.WebGLRenderer({canvas: canvas.get(0), alpha: true, antialias: true}); + useWebGlRenderer = true; + + // Show performance notice if using software rendering + if (webglResult.method === 'software') { + GUI.log('3D view using software rendering (slower). Consider updating graphics drivers or disabling hardware acceleration in Options.'); + } + } catch (e) { + console.error('[3D] Failed to create THREE.WebGLRenderer:', e); + renderer = null; + useWebGlRenderer = false; + } + } + + // Check if WebGL is available + if (!renderer) { + // WebGL not supported - show fallback message + wrapper.html('
' + + '
' + + '

3D view unavailable

' + + '

WebGL could not be initialized. This may be due to:

' + + '
    ' + + '
  • Graphics drivers need updating
  • ' + + '
  • Hardware acceleration issues
  • ' + + '
  • Browser or system limitations
  • ' + + '
' + + '

Try: Options → Disable 3D Hardware Acceleration, then restart

' + + '
' + + '
'); + + // Provide no-op functions so the rest of the tab doesn't break + this.render3D = function () {}; + this.resize3D = function () {}; + return; + } + // initialize render size for current canvas size renderer.setSize(wrapper.width()*2, wrapper.height()*2);