Canvas text functions are too slow

NEW
Unassigned

Status

()

Core
Canvas: 2D
--
major
8 years ago
3 years ago

People

(Reporter: Tyler Thompson, Unassigned)

Tracking

({testcase})

Firefox Tracking Flags

(Not tracked)

Details

(URL)

Attachments

(7 attachments)

(Reporter)

Description

8 years ago
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
(Reporter)

Comment 1

8 years ago
Created attachment 411108 [details]
Firefox 3.6b1 results
(Reporter)

Comment 2

8 years ago
Created attachment 411109 [details]
Safari 4 results
(Reporter)

Comment 3

8 years ago
Created attachment 411110 [details]
Speed test
(Reporter)

Comment 4

8 years ago
Created attachment 411111 [details]
Windows profile
(Reporter)

Comment 5

8 years ago
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(' ');
}
(Reporter)

Comment 6

8 years ago
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.
(Reporter)

Comment 7

8 years ago
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();

Updated

8 years ago
Severity: enhancement → major

Comment 8

7 years ago
Created attachment 439750 [details]
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

Comment 9

7 years ago
Created attachment 439751 [details]
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.
Created attachment 542504 [details]
KothicJS rendering screenshot

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.

Comment 12

5 years ago
A JS application I’ve made for Chrome is unusable in Firefox due to this problem.

Updated

5 years ago
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

Comment 14

5 years ago
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.

Updated

4 years ago
Blocks: 934525

Comment 15

3 years ago
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
You need to log in before you can comment on or make changes to this bug.