fix(web): clamp /play canvas to GPU MAX_TEXTURE_SIZE to stop wgpu panic

Rhys hit a fatal wgpu validation panic loading /play on a 1440p display:

    Surface::configure ... Requested was (2560, 1440), maximum extent for
    either dimension is 2048

The earlier scale_factor_override(1.0) fix only neutralised HiDPI (CSS×DPR);
it didn't help when the *logical* viewport itself exceeds 2048. `fit_canvas_to_
parent` sizes the wgpu surface to the canvas's parent, so a 2560-wide viewport
configures a 2560-wide surface — past WebGL2's 2048 per-dimension limit on
laptop/integrated GPUs, and the panic kills the WASM thread on the first frame.

Wrap the canvas in #bevy-wrap and, before init, clamp that element's max-
width/height to the device's actual gl.MAX_TEXTURE_SIZE (falling back to the
2048 WebGL2 floor). fit_canvas_to_parent then never produces a surface larger
than the GPU allows. Only devices at the 2048 floor letterbox (centered); GPUs
that report 4096/8192 still fill the viewport.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-23 17:44:46 -07:00
parent 923a67dc7b
commit 7b5d69e164
+35 -2
View File
@@ -7,11 +7,44 @@
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: #000; overflow: hidden; }
#bevy-canvas { display: block; width: 100vw; height: 100vh; }
/* #bevy-wrap fills the viewport but is capped to the GPU's real
MAX_TEXTURE_SIZE via an inline max-width/height set before init
(see the clamp script below). fit_canvas_to_parent sizes the wgpu
surface to this element, so capping it keeps the surface within
WebGL2's per-dimension limit. Centered so 2048-limited devices
letterbox symmetrically. */
#bevy-wrap {
width: 100vw; height: 100vh; margin: 0 auto;
display: flex; align-items: center; justify-content: center;
}
#bevy-canvas { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<canvas id="bevy-canvas"></canvas>
<div id="bevy-wrap"><canvas id="bevy-canvas"></canvas></div>
<script>
// Clamp the Bevy canvas's backing buffer to the device's real WebGL2
// MAX_TEXTURE_SIZE *before* the wasm initialises. `fit_canvas_to_parent`
// sizes the wgpu surface to #bevy-wrap; a surface larger than the GPU
// limit (only 2048 on many laptop/integrated GPUs — e.g. a 2560-wide
// 1440p display) makes Surface::configure panic and kills the WASM
// thread. Querying the real maximum means only devices at the 2048 floor
// letterbox; everyone else still fills the viewport. Runs synchronously
// before the deferred module script below.
(function clampCanvasToGpuLimit() {
var max = 2048; // WebGL2 spec floor — the safe fallback
try {
var probe = document.createElement("canvas").getContext("webgl2");
if (probe) {
var m = probe.getParameter(probe.MAX_TEXTURE_SIZE);
if (m && m > 0) max = m;
}
} catch (e) { /* keep the safe 2048 floor */ }
var wrap = document.getElementById("bevy-wrap");
wrap.style.maxWidth = max + "px";
wrap.style.maxHeight = max + "px";
})();
</script>
<script type="module">
import init from "/web/pkg/canvas.js";
// solitaire_wasm.js provides SolitaireGame with the full debug / automation