From ae9b51c3f148accd4d5d49eb4138ff4dc4dd0935 Mon Sep 17 00:00:00 2001 From: "Allegro (Burn Mode)" Date: Sun, 5 Apr 2026 11:36:40 +0000 Subject: [PATCH] perf(nexus): implement performance tier optimization for low-end devices --- app.js | 65 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/app.js b/app.js index 4147640..f469546 100644 --- a/app.js +++ b/app.js @@ -644,19 +644,31 @@ async function init() { playerPos = new THREE.Vector3(0, 2, 12); playerRot = new THREE.Euler(0, 0, 0, 'YXZ'); + // Detect performance tier BEFORE creating renderer (antialias cannot be changed after) + performanceTier = detectPerformanceTier(); + const canvas = document.getElementById('nexus-canvas'); - renderer = new THREE.WebGLRenderer({ canvas, antialias: true }); + const antialias = performanceTier !== 'low'; + renderer = new THREE.WebGLRenderer({ canvas, antialias }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.2; - renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.PCFSoftShadowMap; + renderer.shadowMap.enabled = performanceTier !== 'low'; + renderer.shadowMap.type = performanceTier === 'high' ? THREE.PCFSoftShadowMap : THREE.BasicShadowMap; - performanceTier = detectPerformanceTier(); updateLoad(10); scene = new THREE.Scene(); - scene.fog = new THREE.FogExp2(0x050510, 0.012); + + // Fog density based on performance tier + if (performanceTier === 'low') { + // No fog for low tier + scene.fog = null; + } else if (performanceTier === 'medium') { + scene.fog = new THREE.FogExp2(0x050510, 0.008); + } else { + scene.fog = new THREE.FogExp2(0x050510, 0.012); + } setupGOFAI(); camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 1000); @@ -710,14 +722,29 @@ async function init() { fetchGiteaData(); setInterval(fetchGiteaData, 30000); // Refresh every 30s - composer = new EffectComposer(renderer); - composer.addPass(new RenderPass(scene, camera)); - const bloom = new UnrealBloomPass( - new THREE.Vector2(window.innerWidth, window.innerHeight), - 0.6, 0.4, 0.85 - ); - composer.addPass(bloom); - composer.addPass(new SMAAPass(window.innerWidth, window.innerHeight)); + // Initialize post-processing based on performance tier + // Low tier: No post-processing (avoids GPU stalls from readPixels) + // Medium tier: RenderPass + SMAA only (no bloom) + // High tier: Full post-processing with bloom + if (performanceTier === 'low') { + // No composer for low tier - use direct renderer.render() + composer = null; + } else if (performanceTier === 'medium') { + composer = new EffectComposer(renderer); + composer.addPass(new RenderPass(scene, camera)); + // Skip UnrealBloomPass to avoid GPU stalls from readPixels + composer.addPass(new SMAAPass(window.innerWidth, window.innerHeight)); + } else { + // High tier: Full post-processing with bloom + composer = new EffectComposer(renderer); + composer.addPass(new RenderPass(scene, camera)); + const bloom = new UnrealBloomPass( + new THREE.Vector2(window.innerWidth, window.innerHeight), + 0.6, 0.4, 0.85 + ); + composer.addPass(bloom); + composer.addPass(new SMAAPass(window.innerWidth, window.innerHeight)); + } updateLoad(95); @@ -2495,7 +2522,12 @@ function gameLoop() { core.material.emissiveIntensity = 1.5 + Math.sin(elapsed * 2) * 0.5; } - composer.render(); + // Render based on performance tier (low tier bypasses composer to avoid GPU stalls) + if (composer) { + composer.render(); + } else { + renderer.render(scene, camera); + } updateAshStorm(delta, elapsed); updatePortalTunnel(delta, elapsed); @@ -2528,7 +2560,10 @@ function onResize() { camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); - composer.setSize(w, h); + // Only resize composer if it exists (low tier has no composer) + if (composer) { + composer.setSize(w, h); + } } // ═══ AGENT SIMULATION ═══