Closed
Bug 601176
Opened 14 years ago
Closed 4 months ago
Slow performance on ctx.fillRect
Categories
(Core :: Graphics: Canvas2D, defect)
Tracking
()
RESOLVED
INACTIVE
Tracking | Status | |
---|---|---|
blocking2.0 | --- | - |
People
(Reporter: jandem, Unassigned)
References
(Blocks 1 open bug)
Details
(Keywords: perf, Whiteboard: [painting-perf])
Attachments
(1 file)
421 bytes,
text/html
|
Details |
ctx.fillRect is very slow in Firefox. Numbers for the attached test case on OS X:
Chrome: 18 ms
Safari: 48 ms
FF4 nightly: 136 ms
Shark shows we're creating a path and calling CGContextDrawPath. Safari just calls CGContextFillRect.
bz said this issue has come up before and asked to request blocking2.0.
Reporter | ||
Updated•14 years ago
|
blocking2.0: --- → ?
Comment 1•14 years ago
|
||
To clarify, FillRect performance is one of our most common Canvas bottlenecks. We seem to be converting it to a path, etc, and then going through Cairo and CG path stuff on Mac; not sure about other OSes.
Especially optimizing 1x1 fillRect (which pages use when they should perhaps use imageData) would be really nice.
Updated•14 years ago
|
Whiteboard: [painting-perf]
Comment 2•14 years ago
|
||
I'd take a patch here, but we already have a lot of work to do for Fx4.
blocking2.0: ? → -
Comment 3•14 years ago
|
||
(In reply to comment #0)
> Shark shows we're creating a path and calling CGContextDrawPath. Safari just
> calls CGContextFillRect.
Bug 516931 is where this has come up before.
Comment 4•14 years ago
|
||
Fun times, yeah.
The point is, whatever we're doing here Chrome is managing to do it about 7x faster and Safari 3x faster....
Comment 5•14 years ago
|
||
Safari is spending almost all of its fillRect time in CGContextFillRect, whereas cairo wastes almost 50% in setup_state and teardown_state saving and restoring contexts and resetting colors. Path management in nsCanvasRenderingContext2D::DrawRect also takes another 10% before we even enter nsCanvasRenderingContext2D::DrawPath.
(The percentages above are relative to the time spent under nsCanvasRenderingContext2D::FillRect.)
The cairo setup/teardown stuff can probably be fixed inside the cairo quartz implementation with more sophisticated state tracking. Avoiding the path management overhead probably requires a new fillRect cairo API.
The cairo quartz surface should be looking for paths that form a rect, and calling FillRect if possible -- it doesn't seem to be doing that now. That check is fairly cheap, and doesn't require adding new cairo API. We'll still have overhead of setup_state, though even that could be optimized for some cases.
There's also the possibility of tracking state more aggressively in the quartz surface; that would be a useful thing to do.
Comment 7•14 years ago
|
||
(In reply to comment #6)
> The cairo quartz surface should be looking for paths that form a rect, and
> calling FillRect if possible -- it doesn't seem to be doing that now. That
> check is fairly cheap, and doesn't require adding new cairo API.
This was tried in bug 516931, unsuccessfully.
The 10% path management overhead that I was referring to are things like gfxContext::Rectangle, gfxContext::CopyPath, and allocating and releasing gfxPath objects.
Comment 8•14 years ago
|
||
The plan for fixing the state management problems is cairo_backend_t http://cgit.freedesktop.org/~ickle/cairo/log/?h=cairo_backend_t
Its also slow on Windows XP. My numbers from the attachment:
Chrome: 28
Safari: 46
F4: 65
Peacekeeper uses this function a lot in its canvas tests so speeding it up would help there.
Comment 10•14 years ago
|
||
Can this be marked as blocking 499198?
Updated•14 years ago
|
Blocks: peacekeeper
Comment 11•13 years ago
|
||
With FF8 Nightly "http://hg.mozilla.org/mozilla-central/rev/be4b064f1159":
On Test case:
FF8 Nightly: 30 ms
Chrome 14.0.835.15 Dev: 14 ms
Test was performed on my main profile, not on fresh profile.
Comment 12•13 years ago
|
||
Fx is much faster than it used to be thanks most likely to the new Azure backend. However were still twice as slow as Chrome. Any ideas why?
Comment 13•13 years ago
|
||
There is no Azure backend on Mac. This bug is about a Mac-specific issue.
Please file a separate bug on any slowness you see on Windows and cc me on it?
Comment 14•11 years ago
|
||
This should have improved significantly since this bug was filed. We now call directly into CGFillRect on OS X. That being said there's still some overhead that we have beyond what Safari would have. The bulk of that is CGContextSaveState()/CGContextRestoreState(). This can be eliminated by setting the matrix directly instead of relying on Concatenation.
Reporter | ||
Comment 15•11 years ago
|
||
(In reply to Jeff Muizelaar [:jrmuizel] from comment #14)
> This should have improved significantly since this bug was filed. We now
> call directly into CGFillRect on OS X. That being said there's still some
> overhead that we have beyond what Safari would have. The bulk of that is
> CGContextSaveState()/CGContextRestoreState(). This can be eliminated by
> setting the matrix directly instead of relying on Concatenation.
Do you know somebody who can take this bug? Although this improved a lot, on OS X the attached micro-benchmark is still 2-3x slower than Chrome and the Peacekeeper ripple tests (bug 919992) are mostly fillRect bound.. :)
Flags: needinfo?(jmuizelaar)
Comment 16•11 years ago
|
||
(In reply to Jan de Mooij [:jandem] from comment #15)
> (In reply to Jeff Muizelaar [:jrmuizel] from comment #14)
> > This should have improved significantly since this bug was filed. We now
> > call directly into CGFillRect on OS X. That being said there's still some
> > overhead that we have beyond what Safari would have. The bulk of that is
> > CGContextSaveState()/CGContextRestoreState(). This can be eliminated by
> > setting the matrix directly instead of relying on Concatenation.
>
> Do you know somebody who can take this bug? Although this improved a lot, on
> OS X the attached micro-benchmark is still 2-3x slower than Chrome and the
> Peacekeeper ripple tests (bug 919992) are mostly fillRect bound.. :)
The peacekeeper ripple tests are not really a use case that's important for real content and this problem will only show up on OS X (which isn't a common benchmark target). So this is a pretty low priority.
Flags: needinfo?(jmuizelaar)
Comment 17•10 years ago
|
||
This is not OSX only. We're a lot slower on linux too.
OS: Mac OS X → Other
Comment 18•3 years ago
|
||
Hello everyone, in 2021 both fillRect() and clearRect() are super slow in Firefox on MacOS. I'm drawing a huge heatmap calendar that spans ~60 canvas elements. Just clearRect-ing / fillRect-ing those 60 canvases take %60 of the time compared to everything else that is needed to draw a heatmap calendar.
Comment 19•3 years ago
|
||
Here's a self-contained HTML that you can run in FF for MacOS and see the difference between fillRect()
and webgl's clear()
. With all plugins OFF in FF, to me it shows:
100 tests
fillRect(): 229.87ms
webgl: 0.06ms
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<p id='results'>
Results will appear here
</p>
<canvas style="width: 300px; height: 300px; border: 1px solid orange" id='standard_context_canvas'></canvas>
<canvas style="width: 300px; height: 300px; border: 1px solid green" id='webgl_context_canvas'></canvas>
<script>
const num_tests = 100;
const standard_context_canvas = document.getElementById('standard_context_canvas');
const webgl_context_canvas = document.getElementById('webgl_context_canvas');
const ctx = standard_context_canvas.getContext('2d');
const gl = webgl_context_canvas.getContext("webgl") || webgl_context_canvas.getContext("experimental-webgl");
// Preparation
const canvas_width_height = 3000;
standard_context_canvas.width = canvas_width_height;
standard_context_canvas.height = canvas_width_height;
gl.viewport(0, 0, canvas_width_height, canvas_width_height);
console.log(`WebGL Viewport: ` + JSON.stringify(gl.getParameter(gl.VIEWPORT)));
const measurements = [];
function measure(name, callback_to_reset, callback_to_measure) {
let total_time_spent_ms = 0;
for (let i = 0; i < num_tests; i++) {
callback_to_reset();
const start_time = performance.now();
callback_to_measure();
const time_spent_ms = performance.now() - start_time;
total_time_spent_ms += time_spent_ms;
}
measurements.push({name, time_spent_ms: total_time_spent_ms});
}
measure('fillRect()', () => {
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, canvas_width_height, canvas_width_height);
}, () => {
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas_width_height, canvas_width_height);
});
measure('webgl', () => {
// Set clear color to red, fully opaque
gl.clearColor(1.0, 0.0, 0.0, 1.0);
// Clear the color buffer with specified clear color
gl.clear(gl.COLOR_BUFFER_BIT);
}, () => {
// Set clear color to white, fully opaque
gl.clearColor(1.0, 1.0, 1.0, 1.0);
// Clear the color buffer with specified clear color
gl.clear(gl.COLOR_BUFFER_BIT);
});
// Output the measurements results
let results_text = [];
for (let i = 0; i < measurements.length; i++) {
const {name, time_spent_ms} = measurements[i];
results_text.push(name + ': ' + time_spent_ms.toFixed(2) + 'ms');
}
results_text = num_tests + ' tests<br/>'
+ results_text.join('<br/>')
const results_paragraph = document.getElementById('results');
results_paragraph.innerHTML = results_text;
</script>
</body>
</html>
Updated•2 years ago
|
Severity: normal → S3
Comment 20•4 months ago
|
||
I increased loop counts to 100x and 10x. These are the results on Win11x64:
D2d-canvas: https://share.firefox.dev/3X4fxTf (5456ms without the profiler)
Gpu-canvas: https://share.firefox.dev/3YNOYmK (1800ms without the profiler)
Chrome: 1320ms
Updated•4 months ago
|
Status: NEW → RESOLVED
Closed: 4 months ago
Resolution: --- → INACTIVE
You need to log in
before you can comment on or make changes to this bug.
Description
•