Last Comment Bug 638549 - Add about:memory accounting to WebGL
: Add about:memory accounting to WebGL
Status: RESOLVED FIXED
[MemShrink:P2]
:
Product: Core
Classification: Components
Component: Canvas: WebGL (show other bugs)
: unspecified
: All All
: -- normal (vote)
: ---
Assigned To: Benoit Jacob [:bjacob] (mostly away)
:
Mentors:
: 655363 (view as bug list)
Depends on:
Blocks: DarkMatter 657115 637449 643651 648411 651695
  Show dependency treegraph
 
Reported: 2011-03-03 12:55 PST by Jeff Muizelaar [:jrmuizel]
Modified: 2011-08-10 15:57 PDT (History)
15 users (show)
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---


Attachments
part 1: initial implementation accounting for WebGLContexts and WebGLTextures (11.49 KB, patch)
2011-06-30 21:58 PDT, Benoit Jacob [:bjacob] (mostly away)
jmuizelaar: review+
Details | Diff | Review
part 2: add accounting for WebGLBuffers (5.83 KB, patch)
2011-06-30 21:59 PDT, Benoit Jacob [:bjacob] (mostly away)
jmuizelaar: review+
Details | Diff | Review
part 3: add accounting for WebGLRenderbuffers (11.18 KB, patch)
2011-07-02 16:40 PDT, Benoit Jacob [:bjacob] (mostly away)
jmuizelaar: review+
Details | Diff | Review
part 4: add accounting for WebGLBbuffer caches (6.15 KB, patch)
2011-07-02 17:03 PDT, Benoit Jacob [:bjacob] (mostly away)
jmuizelaar: review+
Details | Diff | Review
part 5: add accounting for WebGLShaders (7.76 KB, patch)
2011-07-02 21:43 PDT, Benoit Jacob [:bjacob] (mostly away)
jmuizelaar: review+
Details | Diff | Review
fix reporters leak (2.03 KB, patch)
2011-07-27 12:09 PDT, Benoit Jacob [:bjacob] (mostly away)
no flags Details | Diff | Review
fix reporters leak (2.03 KB, patch)
2011-07-28 07:17 PDT, Benoit Jacob [:bjacob] (mostly away)
jmuizelaar: review+
Details | Diff | Review

Description Jeff Muizelaar [:jrmuizel] 2011-03-03 12:55:46 PST

    
Comment 1 Nicholas Nethercote [:njn] (on vacation until July 11) 2011-05-12 02:57:32 PDT
*** Bug 655363 has been marked as a duplicate of this bug. ***
Comment 2 Benoit Jacob [:bjacob] (mostly away) 2011-06-30 21:56:54 PDT
I've actually had much of theses patches for a while, but I didn't finish them and put them up for review because I hesitated on the approach.

These patches rely on keeping track only of WebGLContexts, and letting each WebGLContext enumerate its objects (such as textures). The advantage is that this is the simplest to implement, but the disadvantage is that this wouldn't account for leaked objects (such as textures) that would no longer be referenced from any WebGLContext.

For this reason I then contemplated a different approach based on plain counters updated by the objects themselves, e.g. a texture memory usage counter updated by the WebGLTexture objects when their buffers are allocated/reallocated/freed. I got stuck there for a very long time as this is very inconvenient to implement. In particular, this was inconvenient for mipmapped textures as our WebGLTexture class tries to take advantage of knowledge that a mipmap has been autogenerated to simplify its bookkeeping, so switching to/from auto-generated-mipmap state would result in complicated updates to the texture memory usage metric.

Moreover the above-mentioned problem with the initial approach, namely that it would account for textures no longer referenced from any context, turns out to not matter in practice, at least for now: a look at the code shows that there is little risk that a WebGLTexture could not be referenced from a context, since WebGLTextures can only be created by WebGLContext::createTexture which stores a reference to the newly created texture in the WebGLContext. This is currently a strong reference, by the way, and we may want to let that be a weak reference in order to help freeing texture memory more aggressively in case of unreferenced textures, even though the canonical way of freeing texture memory in WebGL is to explicitly call the deleteTexture() function.
Comment 3 Benoit Jacob [:bjacob] (mostly away) 2011-06-30 21:58:13 PDT
Created attachment 543345 [details] [diff] [review]
part 1: initial implementation accounting for WebGLContexts and WebGLTextures
Comment 4 Benoit Jacob [:bjacob] (mostly away) 2011-06-30 21:59:18 PDT
Created attachment 543346 [details] [diff] [review]
part 2: add accounting for WebGLBuffers

coming soon: part 3 for renderbuffers
Comment 5 Benoit Jacob [:bjacob] (mostly away) 2011-06-30 22:11:14 PDT
Try:
http://tbpl.mozilla.org/?tree=Try&rev=c2df6455c534
Comment 6 Nicholas Nethercote [:njn] (on vacation until July 11) 2011-06-30 22:23:18 PDT
(In reply to comment #2)
> 
> These patches rely on keeping track only of WebGLContexts, and letting each
> WebGLContext enumerate its objects (such as textures). The advantage is that
> this is the simplest to implement, but the disadvantage is that this
> wouldn't account for leaked objects (such as textures) that would no longer
> be referenced from any WebGLContext.
> 
> For this reason I then contemplated a different approach based on plain
> counters updated by the objects themselves, e.g. a texture memory usage
> counter updated by the WebGLTexture objects when their buffers are
> allocated/reallocated/freed.

I think the former approach (compute from scratch) is better than the latter approach (maintain some counters).  See bug 663271 comment 0 for more.  So I'm pleased you went with the former approach!
Comment 7 Benoit Jacob [:bjacob] (mostly away) 2011-06-30 22:29:30 PDT
Thanks for the link. Yes, the concern outlined there echoes mine.
Comment 8 Benoit Jacob [:bjacob] (mostly away) 2011-07-01 08:55:53 PDT
Try builds are ready at:
https://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/bjacob@mozilla.com-c2df6455c534/
Comment 9 Jeff Muizelaar [:jrmuizel] 2011-07-01 10:32:13 PDT
Comment on attachment 543345 [details] [diff] [review]
part 1: initial implementation accounting for WebGLContexts and WebGLTextures

>+    static PRInt64 GetTextureMemoryUsed() {
>+        const ContextsArrayType & contexts = Contexts();
>+        PRInt64 result = 0;
>+        for(size_t i = 0; i < contexts.Length(); ++i) {
>+            PRInt64 textureMemoryUsageForThisContext = 0;

I think I would prefer to account memory on creation and deletion instead of using enumeration. That way we're more likely to have leaks show up, but this is fine for now provided you add a comment to the enumeration code that suggests the alternative.


>+PRUint32 WebGLContext::GetTexelSize(WebGLenum format, WebGLenum type)
>+{
>+    if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT) {
>+        int multiplier = type == LOCAL_GL_FLOAT ? 4 : 1;

I think that channelSize or channelBytes is a better name than multiplier.
Comment 10 Jeff Muizelaar [:jrmuizel] 2011-07-01 10:34:13 PDT
Comment on attachment 543346 [details] [diff] [review]
part 2: add accounting for WebGLBuffers

We'll eventually probably want to split these out by context, but we can wait until the about:compartments stuff lands to get an idea of the best way to expose that.
Comment 11 Nicholas Nethercote [:njn] (on vacation until July 11) 2011-07-01 14:23:16 PDT
(In reply to comment #10)
> 
> We'll eventually probably want to split these out by context, but we can
> wait until the about:compartments stuff lands to get an idea of the best way
> to expose that.

about:compartments (bug 625305) will never land.  Per-compartment reporters in about:memory (bug 661474) landed this week.
Comment 12 Benoit Jacob [:bjacob] (mostly away) 2011-07-01 14:37:05 PDT
Is there an easy way that I can adapt my patches to make them use per-compartment reporters? Example code?
Comment 13 Nicholas Nethercote [:njn] (on vacation until July 11) 2011-07-01 15:16:43 PDT
Per-compartment reporters make sense for JS because compartments are the primary data structure in the JS engine.  See  XPConnectJSCompartmentsMultiReporter in js/src/xpconnect/src/xpcjsruntime.cpp.  It traverses the entire GC heap, starting at the compartment level and working down to individual objects, recording numbers along the way.

Do per-compartment reporters make sense for WebGL?  Does WebGL's structure use compartments?
Comment 14 Benoit Jacob [:bjacob] (mostly away) 2011-07-01 17:11:20 PDT
WebGL objects have two "sides": they are XPCOM objects (ultimately created by Javascript), and they are OpenGL objects. The WebGL implementation takes care of the correspondence between these two aspects. So it doesn't matter that OpenGL objects by themselves don't care about compartments, the XPCOM side of each WebGL object should know what compartment it was associated with, just like any other JS-created object, right? Does that make sense?
Comment 15 Nicholas Nethercote [:njn] (on vacation until July 11) 2011-07-01 21:55:22 PDT
Yes, that makes sense.  If you are given any random JSObject, can you tell if it's a WebGL object?  If so, you can modify CellCallback (http://mxr.mozilla.org/mozilla-central/source/js/src/xpconnect/src/xpcjsruntime.cpp#1472) accordingly.  Note that CellCallback is part of an nsIMemoryMultiReporter, the interface for which is a bit different to nsIMemoryReporter.
Comment 16 Benoit Jacob [:bjacob] (mostly away) 2011-07-02 11:32:08 PDT
Each WebGL object type implements a specific XPCOM interface so could be identified by a QueryInterface. However, looking that this CellCallBack code, it's not obvious to me how/where to do this, and also it's not clear to me why something specific has to be done for WebGL objects there whereas other JS objects don't seem to require anything specific there (this CellCallBack function does not have a lot of special cases for all sorts of objects already).
Comment 17 Nicholas Nethercote [:njn] (on vacation until July 11) 2011-07-02 15:03:10 PDT
CellCallback doesn't do anything depending on the type of the object (with the exception of strings, which have a different kind anyway).  If I wanted to count RegExp objects separately, for example, then I'd have to do more querying on the object kind.  And I thought you might be able to do something similar for WebGL objects.  But I don't know if you can call QueryInterface on a JSObject.  Hmm.

Is there another way to find WebGL objects other than crawling the heap and inspecting every object?  Eg. can you iterate over the WebGL contexts and find WebGL objects that way?  For any given JSObject you can find its compartment with Cell::compartment(), because JSObject is a sub-class of Cell.
Comment 18 Benoit Jacob [:bjacob] (mostly away) 2011-07-02 15:36:21 PDT
(In reply to comment #17)
> CellCallback doesn't do anything depending on the type of the object (with
> the exception of strings, which have a different kind anyway).  If I wanted
> to count RegExp objects separately, for example, then I'd have to do more
> querying on the object kind.  And I thought you might be able to do
> something similar for WebGL objects.  But I don't know if you can call
> QueryInterface on a JSObject.  Hmm.

FYI, the JSAPI allows you to convert JSObjects to XPCOM objects implementing a given interface, see e.g. xpc_qsUnwrapArg, see how it's used in CustomQS_WebGL.h.

> 
> Is there another way to find WebGL objects other than crawling the heap and
> inspecting every object?  Eg. can you iterate over the WebGL contexts and
> find WebGL objects that way?

Sure! That's exactly what my patches here are doing. The WebGLMemoryReporter implemented here maintains an array of WebGLContext*'s that is updated everytime a context is created or deleted. Each context is able to list its associated objects such as textures, etc.

> For any given JSObject you can find its
> compartment with Cell::compartment(), because JSObject is a sub-class of
> Cell.

My problem here is that by the time I can enumerate my WebGL objects, these are XPCOM objects, not JS objects. I would need an interface allowing me to get, for a given XPCOM objects, a JS object corresponding to it (I do not know if that would be unique, but that doesn't matter as all JS objects corresponding to it would belong to the same compartment).
Comment 19 Benoit Jacob [:bjacob] (mostly away) 2011-07-02 16:40:17 PDT
Created attachment 543633 [details] [diff] [review]
part 3: add accounting for WebGLRenderbuffers

Also allowed myself to expand the description for textures and buffers. Please dont r- just for that.
Comment 20 Benoit Jacob [:bjacob] (mostly away) 2011-07-02 17:03:06 PDT
Created attachment 543634 [details] [diff] [review]
part 4: add accounting for WebGLBbuffer caches

WebGLBuffer stores on the heap a copy of the buffer data in case of element array buffers. That seemed worth reporting. I also double checked the implementation that we don't have a stupid leak there that wouldn't be accounted for.
Comment 21 Benoit Jacob [:bjacob] (mostly away) 2011-07-02 21:43:36 PDT
Created attachment 543650 [details] [diff] [review]
part 5: add accounting for WebGLShaders

That should be it. Shaders store two C strings: the source, and the translation log. Both would be interesting to know if they get abnormally high. Large source size would indicate a WebGL page with absurdly big shaders; large translation logs could happen if shader compiler has gone crazy, or could indicate that there is something weird in a shader.
Comment 22 Benoit Jacob [:bjacob] (mostly away) 2011-07-02 21:47:52 PDT
Note that I've been a bit aggressive with adding probes as I have no idea, if the WebGL leak bug reports are real, where the leak is, so I'm adding probes on everything that could conceivably eat memory. It will be time to remove useless probes once concerns of WebGL leaks are gone, I wish we didn't have worse problems than useless probes. Also, these only show up in about:memory if WebGL has been used, so it won't disturb too much people who don't care.
Comment 23 Benoit Jacob [:bjacob] (mostly away) 2011-07-02 22:05:25 PDT
Try:
http://tbpl.mozilla.org/?tree=Try&rev=087d304b9757
Comment 25 Matthew Gregan [:kinetik] 2011-07-21 20:24:36 PDT
Shouldn't the nsIMemoryReporter instances be held in nsCOMPtrs?  Otherwise, I can't see how they are ever freed.  This was pointed out to me by Nick in bug 672755 comment 10, where I copied the same bug into my code.
Comment 26 Benoit Jacob [:bjacob] (mostly away) 2011-07-27 12:02:09 PDT
Thanks for the catch. Making them nsRefPtrs, as I don't see the need for nsCOMPtr here.
Comment 27 Benoit Jacob [:bjacob] (mostly away) 2011-07-27 12:09:51 PDT
Created attachment 548864 [details] [diff] [review]
fix reporters leak
Comment 28 Nicholas Nethercote [:njn] (on vacation until July 11) 2011-07-27 16:43:11 PDT
(In reply to comment #26)
> Making them nsRefPtrs, as I don't see the need for nsCOMPtr here.

My understanding is that you use nsRefPtr for concrete classes, but nsCOMPtr for abstract classes, and this is the latter.
Comment 29 Josh Matthews [:jdm] 2011-07-27 16:48:09 PDT
Nicholas is correct. nsRefPtr can cause build errors when used with certain abstract XPCOM interfaces, so prefer nsCOMPtr if it's an interface type.
Comment 30 Benoit Jacob [:bjacob] (mostly away) 2011-07-28 07:17:27 PDT
Created attachment 549101 [details] [diff] [review]
fix reporters leak
Comment 31 Benoit Jacob [:bjacob] (mostly away) 2011-08-10 15:57:14 PDT
http://hg.mozilla.org/mozilla-central/rev/55b4a5e8b5a3

Note You need to log in before you can comment on or make changes to this bug.