blob: 66a947b60339468f1f6c6e8d51647c1215f0d35a [file]
<!DOCTYPE html>
<html>
<head><title>Y16 texSubImage2D UNPACK_ROW_LENGTH OOB Test</title></head>
<body>
<video id="v" autoplay playsinline muted></video>
<canvas id="c" width="8" height="8"></canvas>
<pre id="log"></pre>
<script>
const log = m => { document.getElementById('log').textContent += m + '\n'; console.log(m); };
async function run() {
// 1. Open the Y16 depth-style camera. With Chrome's fake-device factory and
// device-count=2, device index 1 (label "fake_device_1") emits
// PIXEL_FORMAT_Y16 frames -- the same pipeline a RealSense camera uses.
await navigator.mediaDevices.getUserMedia({video: true}); // prime labels
const devs = await navigator.mediaDevices.enumerateDevices();
for (const d of devs) log(`dev: kind=${d.kind} label="${d.label}" id=${d.deviceId}`);
const y16 = devs.find(d => d.kind === 'videoinput' && /fake_device_1/.test(d.label));
if (!y16) {
log('FAIL: no Y16 fake device found (need device-count>=2)');
if (window.domAutomationController) {
window.domAutomationController.send('failed: no Y16 fake device found');
}
return;
}
const stream = await navigator.mediaDevices.getUserMedia({
video: { deviceId: { exact: y16.deviceId }, width: 96, height: 96 }
});
const v = document.getElementById('v');
v.srcObject = stream;
await new Promise(r => v.onloadeddata = r);
await v.play().catch(()=>{});
// wait for an actual frame to land in the compositor
await new Promise(r => {
if ('requestVideoFrameCallback' in v) v.requestVideoFrameCallback(()=>r());
else setTimeout(r, 500);
});
const W = v.videoWidth, H = v.videoHeight;
log(`video ${W}x${H}`);
// 2. WebGL2 context + destination texture sized to the video.
const gl = document.getElementById('c').getContext('webgl2');
if (!gl) {
log('FAIL: no WebGL2');
if (window.domAutomationController) {
window.domAutomationController.send('failed: no WebGL2');
}
return;
}
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// Allocate destination storage (R32F so RED/FLOAT sub-upload is legal).
gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32F, W, H, 0, gl.RED, gl.FLOAT, null);
log('texStorage err=0x' + gl.getError().toString(16));
// 3. Set UNPACK_ROW_LENGTH > frame width. Leave UNPACK_FLIP_Y_WEBGL and
// UNPACK_PREMULTIPLY_ALPHA_WEBGL at their defaults (false) so that
// ScopedUnpackParametersResetRestore in the Y16 fast path is a no-op
// and ROW_LENGTH stays active inside the GLES2 client.
const ROW_LENGTH = W + 256;
gl.pixelStorei(gl.UNPACK_ROW_LENGTH, ROW_LENGTH);
log(`UNPACK_ROW_LENGTH=${ROW_LENGTH}`);
// 4. Upload the Y16 video frame for 5 seconds.
const startTime = performance.now();
let frameCount = 0;
while (performance.now() - startTime < 5000) {
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RED, gl.FLOAT, v);
const err = gl.getError();
if (err !== gl.NO_ERROR) {
log(`texSubImage2D err=0x` + err.toString(16));
}
frameCount++;
await new Promise(r => requestAnimationFrame(r));
}
log(`Done ${frameCount} uploads. Reporting completion.`);
if (window.domAutomationController) {
window.domAutomationController.send('SUCCESS');
} else {
log('FAIL: domAutomationController not found');
}
}
run().catch(e => {
log('EXC: ' + e);
if (window.domAutomationController) {
window.domAutomationController.send('exception: ' + e);
}
});
</script>
</body>
</html>