Closed Bug 527386 Opened 15 years ago Closed 7 months ago

Canvas text functions are too slow

Categories

(Core :: Graphics: Canvas2D, defect)

defect

Tracking

()

RESOLVED INACTIVE

People

(Reporter: tthomps, Unassigned)

References

()

Details

(Keywords: testcase)

Attachments

(8 files)

User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2b1) Gecko/20091029 Firefox/3.6b1
Build Identifier: 

The text functions of the 2d canvas -- measureText, fillText, strokeText, the font property, and drawing text shadows -- do not effectively scale to support drawing a large number of strings on the canvas at once.

I am attaching a page that tests the performance of these canvas functions, and two images of the results on my machine, one for Firefox 3.6b1 and one for Safari 4.

Reproducible: Always
Attached image Firefox 3.6b1 results
Attached image Safari 4 results
Attached file Speed test
Attached file Windows profile
One simple thing that can be done to improve speed is to change the space-replacement function.  The current implementation looks like this:

From nsCanvasRenderingContext2D.cpp:

2103 static inline void
2104 TextReplaceWhitespaceCharacters(nsAutoString& str)
2105 {
2106     str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
2107 }

It looks like ReplaceChar iterates through every character in the replace string ("\x09\x0A...") for each character in the input string, which is very inefficient.

I think it would be good to use something like this instead:

PRUnichar *c = str.BeginWriting(); 
PRUnichar *end = str.EndWriting();

for(; c < end; c++)
{
	// replace WHATWG spaces, (0x09, 0x0A, 0x0C, 0x0D), with space char 0x20
	if(*c >= 9 && *c <= 13) // if we want to include 0x0B
		*c = PRUnichar(' ');
}

The above function will replace 0x0B with +U0020 as the current code does, but I do want to note that the HTML5 specification currently doesn't include 0x0B as a character that should be replaced, unless I'm missing something.

Alternatively something like these could be used:

for(; c < end; c++)
{
	if(*c == 0x09 || *c == 0x0A || *c == 0x0C || *c == 0x0D)
		*c = PRUnichar(' ');
}

Or..

const PRInt32 whatwgSpace [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0 };
for(; c < end; c++)
{
	if(*c < 14)
		if(whatwgSpace[*c])
			*c = PRUnichar(' ');
}
I've only done a simple runtime profile at this point, which is shown in the "Windows profile" attachment.  Since it's only a measure of exported functions, it does not give the complete picture of cpu usage here, since a lot of the issues appear to be in internal code.  It does look like some functions, such as UpdateFontList, could be improved a bit to gain some speed.
Another thing of note is that when text is drawn with strokeText or with a shadow, the call to Redraw is not given a bounding box for the operation:

2477     if (aOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL && !doDrawShadow)
2478         return Redraw(mThebes->UserToDevice(boundingBox));
2479 
2480     return Redraw();
Severity: enhancement → major
Attached image Firefox 3.6.4 results
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.4) Gecko/20100413 Firefox/3.6.4

Safe Mode, freshly-open, 1 tab only, XP SP3, AMD Athlon XP 2600+, 1,92 Ghz, 1 GB RAM
Attached image Opera 10.51 results
Opera 10.51 results on same machine as above.
This bug also affects http://kothic.org/js/ in-browser map renderer - it is much slower in Firefox than in any other browser.
When run in Firefox, KothicJS spends most of its time rendering text on canvas.

The result is ≈5 seconds of additional lag per each OpenStreetMap tile.
A JS application I’ve made for Chrome is unusable in Firefox due to this problem.
Jeff, Bas, would you take a look please?

What I see so far on Mac is that the "shadow" case is ludicrously slow...
Status: UNCONFIRMED → NEW
Ever confirmed: true
On my Mac, every test case is slower in Firefox than Chrome. While the 'shadow' case is some 300 times slower on Firefox, 'strokeText' is still some 17 times slower. 'fillRect' is the only test that gives similar results.

FireFox Mac: 217ms, 1405ms, 737ms, 2720ms, 10670ms, 208ms.
Chrome Mac: 213ms, 571ms, 156ms, 1291ms, 38ms, 13ms.
Blocks: 934525
All numbers are ms per frame

Nightly 35 (Win 7 - Intel - D3D11 - OMTC - D2D 1.1)
fillRect 1
fillText 47
measureText 16
font Property 71
shadow 365
strokeText 381

Firefox 32 (Win 7 - Intel - D3D10)
fillRect 2
fillText 42
measureText 13
font Property 60
shadow 359
strokeText 356

Chrome 37
fillRect 6
fillText 8
measureText 3
font Property 12
shadow 10
strokeText 8

Chrome 38
fillRect 1
fillText 6
measureText 4
font Property 10
shadow 10
strokeText 7
On Nightly, at least with the backend on my platform, these numbers are within a few ms of Chrome with the exception of strokeText.

I had a look at resolving that perf issue, but:
CanvasRenderingContext2D.cpp:3492 has a note about CoreGraphics performance requiring individual glyphs to be sent off to the rendering backends (for all backends!), yet removing that and sending the whole buffer made it slower.

Moved on to look at gfx/2d/DrawTargetCairo.cpp and tried cairo_glyph_path with the whole string buffer and that is even slower… it seems like this is blocked by Cairo performance improvements.
 
Nightly 38 (Linux - nvidia - OMTC-off - cairo)
fillRect 0
fillText 6
measureText 3
font Property 13
shadow 20
strokeText 950

OMTC appears to slow strokeText to ~1400ms

Chrome 40
fillRect 0
fillText 2
measureText 1
font Property 5
shadow 4
strokeText 4
When I use skia instead of cario(change gfx.canvas.azure.backends), the time of strokeText is boost up.

nightly 39, linux, nvidia, omtc-off
cario:
 fillRect 1
 fillText 7
 measureText 4
 font Property 14
 shadow 19
 strokeText 479
skia:
 fillRect 1
 fillText 7
 measureText 4
 font Property 14
 shadow 28
 strokeText 40

chrome 40:
 fillRect 0
 fillText 3
 measureText 2
 font Property 5
 shadow 5
 strokeText 4

Performance is still noticeably slower than Chrome: https://jsperf.com/canvas-text-performance

See Also: → 1579513
QA Whiteboard: qa-not-actionable
Attached image windows.png

Here's an anecdotal test: Windows: noticeably

  • shadow takes approx 60x times longer
  • strokeText approx 25x
  • fillText/font property approx 4x

In the process of migrating remaining bugs to the new severity system, the severity for this bug cannot be automatically determined. Please retriage this bug using the new severity system.

Severity: major → --

The severity field is not set for this bug.
:lsalzman, could you have a look please?

For more information, please visit BugBot documentation.

Flags: needinfo?(lsalzman)
Severity: -- → S3
Status: NEW → RESOLVED
Closed: 7 months ago
Flags: needinfo?(lsalzman)
Resolution: --- → INACTIVE
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: