diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /dom/canvas/test/test_capture_throttled.html | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/canvas/test/test_capture_throttled.html')
-rw-r--r-- | dom/canvas/test/test_capture_throttled.html | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/dom/canvas/test/test_capture_throttled.html b/dom/canvas/test/test_capture_throttled.html new file mode 100644 index 0000000000..41cd93a6b9 --- /dev/null +++ b/dom/canvas/test/test_capture_throttled.html @@ -0,0 +1,151 @@ +<!DOCTYPE HTML> +<meta http-equiv="content-type" content="text/html; charset=utf-8" /> + +<title>Canvas2D test: CaptureStream() with throttled rAF</title> + +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="captureStream_common.js"></script> +<link rel="stylesheet" href="/tests/SimpleTest/test.css"> +<body> +<script> +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("Ensuring nothing happens until timing out with good margin"); + +// CaptureStreamTestHelper holding utility test functions. +const h = new CaptureStreamTestHelper2D(); + +async function measureRequestAnimationFrameRate() { + const frameRate = await new Promise(resolve => { + let start; + let count; + const tick = time => { + if (!start) { + start = time; + count = 0; + } else { + count += 1; + } + if (time - start > 1000) { + // One second has passed, break. + resolve(count / ((time - start) / 1000)); + return; + } + window.requestAnimationFrame(tick); + }; + window.requestAnimationFrame(tick); + }); + return frameRate; +} + +async function measureSetTimeoutRate() { + const start = performance.now(); + const COUNT = 5; + for(let i = 0; i < COUNT; ++i) { + await new Promise(resolve => setTimeout(resolve, 0)); + } + return COUNT / ((performance.now() - start) / 1000); +} + +async function measureCanvasCaptureFrameRate(captureRate) { + // Canvas element captured by streams. + const c = h.createAndAppendElement('canvas', 'c'); + + // Since we are in a background tab, the video element won't get composited, + // so we cannot look for a frame count there. Instead we use RTCPeerConnection + // and RTCOutboundRtpStreamStats.framesEncoded. + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + + // Add the canvas.captureStream track. + const ctx = c.getContext('2d'); + const [track] = c.captureStream(captureRate).getTracks(); + const sender = pc1.addTrack(track); + + // Ice candidates signaling + for (const [local, remote] of [[pc1, pc2], [pc2, pc1]]) { + local.addEventListener("icecandidate", ({candidate}) => { + if (!candidate || remote.signalingState == "closed") { + return; + } + remote.addIceCandidate(candidate); + }); + } + + // Offer/Answer exchange + await pc1.setLocalDescription(await pc1.createOffer()); + await pc2.setRemoteDescription(pc1.localDescription); + await pc2.setLocalDescription(await pc2.createAnswer()); + await pc1.setRemoteDescription(pc2.localDescription); + + // Wait for connection + while ([pc1, pc2].some(pc => pc.iceConnectionState == "new" || + pc.iceConnectionState == "checking")) { + await Promise.any( + [pc1, pc2].map(p => new Promise(r => p.oniceconnectionstatechange = r))); + } + for (const [pc, name] of [[pc1, "pc1"], [pc2, "pc2"]]) { + ok(["connected", "completed"].includes(pc.iceConnectionState), + `${name} connection established (${pc.iceConnectionState})`); + } + + // Draw to the canvas + const intervalMillis = 1000 / 60; + const getFrameCount = async () => { + const stats = await sender.getStats(); + const outbound = + [...stats.values()].find(({type}) => type == "outbound-rtp"); + return outbound?.framesEncoded ?? 0; + }; + // Wait for frame count change to ensure sender is working. + while (await getFrameCount() == 0) { + h.drawColor(c, h.green); + await new Promise(resolve => setTimeout(resolve, intervalMillis)); + } + const startFrameCount = await getFrameCount(); + const start = performance.now(); + let end = start; + while(end - start <= 1000) { + h.drawColor(c, h.green); + await new Promise(resolve => setTimeout(resolve, intervalMillis)); + end = performance.now(); + } + const framerate = (await getFrameCount() - startFrameCount) / ((end - start) / 1000); + pc1.close(); + pc2.close(); + return framerate; +} + +(async () => { + // Disable background timer throttling so we can use setTimeout to draw to the + // canvas while the refresh driver is throttled. + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.timeout.enable_budget_timer_throttling", false], + ["dom.min_background_timeout_value", 0], + ["dom.min_background_timeout_value_without_budget_throttling", 0], + ["browser.link.open_newwindow", 3], // window.open in new tab + ], + }); + // Throttle the canvas' refresh driver by opening a new foreground tab + const foregroundTab = window.open("about:blank"); + + // Let the throttling take effect + await new Promise(r => setTimeout(r, 500)); + + const rAFRate = await measureRequestAnimationFrameRate(); + ok(rAFRate < 5, `rAF framerate is at ${rAFRate} fps`); + + const setTimeoutRate = await measureSetTimeoutRate(); + ok(setTimeoutRate > 30, `setTimeout rate is at ${setTimeoutRate} tps`); + + const autoRate = await measureCanvasCaptureFrameRate(); + ok(autoRate > 5, `captureStream() framerate is at ${autoRate} fps`); + + const cappedRate = await measureCanvasCaptureFrameRate(10); + ok(cappedRate > 5, `captureStream(10) framerate is at ${cappedRate} fps`); + + foregroundTab.close(); + SimpleTest.finish(); +})(); +</script> + |