Backed out changeset fccadc284fd9 (bug 1753275) for causing perma mochitest failures @ dom/canvas/test/test_capture_throttled.html CLOSED TREE

This commit is contained in:
Sandor Molnar 2024-03-19 15:47:52 +02:00
parent 45ead03a6d
commit 51479c81df
3 changed files with 65 additions and 37 deletions

View file

@ -1,36 +1,46 @@
<!DOCTYPE html> <!DOCTYPE HTML>
<meta charset="utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Canvas2D test: CaptureStream() with throttled rAF</title> <title>Canvas2D test: CaptureStream() with throttled rAF</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script> <script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="captureStream_common.js"></script> <script src="captureStream_common.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" /> <link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body>
<script> <script>
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout( SimpleTest.requestFlakyTimeout("Ensuring nothing happens until timing out with good margin");
"Ensuring nothing happens until timing out with good margin"
);
// CaptureStreamTestHelper holding utility test functions. // CaptureStreamTestHelper holding utility test functions.
const h = new CaptureStreamTestHelper2D(); const h = new CaptureStreamTestHelper2D();
async function measureRequestAnimationFrameRate() { async function measureRequestAnimationFrameRate() {
const start = await new Promise(r => requestAnimationFrame(r)); const frameRate = await new Promise(resolve => {
for (let count = 1; ; count++) { let start;
const time = await new Promise(r => requestAnimationFrame(r)); let count;
if (time - start > 1000) { const tick = time => {
// One second has passed, break. if (!start) {
return count / ((time - start) / 1000); 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() { async function measureSetTimeoutRate() {
// The average isn't accurate at low counts.
const COUNT = 25;
const start = performance.now(); const start = performance.now();
for (let i = 0; i < COUNT; ++i) { const COUNT = 5;
for(let i = 0; i < COUNT; ++i) {
await new Promise(resolve => setTimeout(resolve, 0)); await new Promise(resolve => setTimeout(resolve, 0));
} }
return COUNT / ((performance.now() - start) / 1000); return COUNT / ((performance.now() - start) / 1000);
@ -38,7 +48,7 @@ async function measureSetTimeoutRate() {
async function measureCanvasCaptureFrameRate(captureRate) { async function measureCanvasCaptureFrameRate(captureRate) {
// Canvas element captured by streams. // Canvas element captured by streams.
const c = h.createAndAppendElement("canvas", "c"); const c = h.createAndAppendElement('canvas', 'c');
// Since we are in a background tab, the video element won't get composited, // 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 // so we cannot look for a frame count there. Instead we use RTCPeerConnection
@ -47,42 +57,59 @@ async function measureCanvasCaptureFrameRate(captureRate) {
const pc2 = new RTCPeerConnection(); const pc2 = new RTCPeerConnection();
// Add the canvas.captureStream track. // Add the canvas.captureStream track.
const ctx = c.getContext("2d"); const ctx = c.getContext('2d');
const [track] = c.captureStream(captureRate).getTracks(); const [track] = c.captureStream(captureRate).getTracks();
const sender = pc1.addTrack(track); const sender = pc1.addTrack(track);
// Ice candidates signaling // Ice candidates signaling
pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate); for (const [local, remote] of [[pc1, pc2], [pc2, pc1]]) {
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate); local.addEventListener("icecandidate", ({candidate}) => {
if (!candidate || remote.signalingState == "closed") {
return;
}
remote.addIceCandidate(candidate);
});
}
// Offer/Answer exchange // Offer/Answer exchange
await pc1.setLocalDescription(); await pc1.setLocalDescription(await pc1.createOffer());
await pc2.setRemoteDescription(pc1.localDescription); await pc2.setRemoteDescription(pc1.localDescription);
await pc2.setLocalDescription(); await pc2.setLocalDescription(await pc2.createAnswer());
await pc1.setRemoteDescription(pc2.localDescription); await pc1.setRemoteDescription(pc2.localDescription);
// Wait for RTP packets to arrive. // Wait for connection
const event = await new Promise(r => pc2.ontrack = r); while ([pc1, pc2].some(pc => pc.iceConnectionState == "new" ||
await new Promise(r => event.track.onunmute = r); 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 // Draw to the canvas
const intervalMillis = 1000 / 60; const intervalMillis = 1000 / 60;
const getFrameCount = async () => { const getFrameCount = async () => {
const stats = await sender.getStats(); const stats = await sender.getStats();
const outbound = [...stats.values()].find( const outbound =
({ type }) => type == "outbound-rtp" [...stats.values()].find(({type}) => type == "outbound-rtp");
); return outbound?.framesEncoded ?? 0;
return outbound?.framesEncoded ?? 0; // See bug 1789768.
}; };
is(await getFrameCount(), 0, "frame count starts at 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(); const start = performance.now();
let end; let end = start;
do { while(end - start <= 1000) {
h.drawColor(c, h.green); h.drawColor(c, h.green);
await new Promise(resolve => setTimeout(resolve, intervalMillis)); await new Promise(resolve => setTimeout(resolve, intervalMillis));
end = performance.now(); end = performance.now();
} while (end - start <= 1000); }
const framerate = (await getFrameCount()) / ((end - start) / 1000); const framerate = (await getFrameCount() - startFrameCount) / ((end - start) / 1000);
pc1.close(); pc1.close();
pc2.close(); pc2.close();
return framerate; return framerate;
@ -121,3 +148,4 @@ async function measureCanvasCaptureFrameRate(captureRate) {
SimpleTest.finish(); SimpleTest.finish();
})(); })();
</script> </script>

View file

@ -163,7 +163,7 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver {
? now ? now
: mLastCaptureTime + TimeDuration::FromMilliseconds( : mLastCaptureTime + TimeDuration::FromMilliseconds(
nsRefreshDriver::DefaultInterval()); nsRefreshDriver::DefaultInterval());
if (next <= now) { if (mLastCaptureTime.IsNull() || next <= now) {
AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {}, AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {},
"CaptureFrame direct while throttled"_ns); "CaptureFrame direct while throttled"_ns);
CaptureFrame(now); CaptureFrame(now);

View file

@ -88,7 +88,7 @@ class TimerDriver : public OutputStreamDriver {
str.AppendPrintf( str.AppendPrintf(
"TimerDriver %staking frame (%sexplicitly requested; after %.2fms; " "TimerDriver %staking frame (%sexplicitly requested; after %.2fms; "
"interval cap %.2fms)", "interval cap %.2fms)",
FrameCaptureRequested(aTime) ? "" : "NOT ", sinceLast >= mFrameInterval ? "" : "NOT ",
mExplicitCaptureRequested ? "" : "NOT ", sinceLast.ToMilliseconds(), mExplicitCaptureRequested ? "" : "NOT ", sinceLast.ToMilliseconds(),
mFrameInterval.ToMilliseconds()); mFrameInterval.ToMilliseconds());
} }