Title: [126283] trunk/Source/WebCore
Revision
126283
Author
[email protected]
Date
2012-08-22 02:19:06 -0700 (Wed, 22 Aug 2012)

Log Message

Web Inspector: [WebGL] Generic framework draft for tracking WebGL resources
https://bugs.webkit.org/show_bug.cgi?id=90597

Patch by Andrey Adaikin <[email protected]> on 2012-08-22
Reviewed by Pavel Feldman.

Wrap WebGL rendering context methods and collect a trace log if we are in capturing mode.
Stubbed code for collecting calls contributing to a WebGL resource state so that we could replay them later.

Typical scenario:
- we wrap a GL context with InjectedScript.wrapWebGLContext() and return a proxy to the inspected page
- the proxy saves all calls necessary to do a replay later - only those that modify a resource's state
- when we turn on capturing mode (InjectedScript.captureFrame), we save all WebGL calls to a trace log

* inspector/InjectedScriptSource.js:
(.):
* inspector/InjectedScriptWebGLModuleSource.js:
(.):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (126282 => 126283)


--- trunk/Source/WebCore/ChangeLog	2012-08-22 09:10:23 UTC (rev 126282)
+++ trunk/Source/WebCore/ChangeLog	2012-08-22 09:19:06 UTC (rev 126283)
@@ -1,5 +1,25 @@
 2012-08-22  Andrey Adaikin  <[email protected]>
 
+        Web Inspector: [WebGL] Generic framework draft for tracking WebGL resources
+        https://bugs.webkit.org/show_bug.cgi?id=90597
+
+        Reviewed by Pavel Feldman.
+
+        Wrap WebGL rendering context methods and collect a trace log if we are in capturing mode.
+        Stubbed code for collecting calls contributing to a WebGL resource state so that we could replay them later.
+
+        Typical scenario:
+        - we wrap a GL context with InjectedScript.wrapWebGLContext() and return a proxy to the inspected page
+        - the proxy saves all calls necessary to do a replay later - only those that modify a resource's state
+        - when we turn on capturing mode (InjectedScript.captureFrame), we save all WebGL calls to a trace log
+
+        * inspector/InjectedScriptSource.js:
+        (.):
+        * inspector/InjectedScriptWebGLModuleSource.js:
+        (.):
+
+2012-08-22  Andrey Adaikin  <[email protected]>
+
         Web Inspector: [WebGL] Add minimum transport protocol from backend to frontend
         https://bugs.webkit.org/show_bug.cgi?id=88973
 

Modified: trunk/Source/WebCore/inspector/InjectedScriptSource.js (126282 => 126283)


--- trunk/Source/WebCore/inspector/InjectedScriptSource.js	2012-08-22 09:10:23 UTC (rev 126282)
+++ trunk/Source/WebCore/inspector/InjectedScriptSource.js	2012-08-22 09:19:06 UTC (rev 126283)
@@ -554,15 +554,29 @@
         return object;
     },
 
+    /**
+     * @param {string} name
+     * @return {Object}
+     */ 
     module: function(name)
     {
         return this._modules[name];
     },
- 
+
+    /**
+     * @param {string} name
+     * @param {string} source
+     * @return {Object}
+     */ 
     injectModule: function(name, source)
     {
         delete this._modules[name];
-        var module = InjectedScriptHost.evaluate("(" + source + ")");
+        var moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
+        if (typeof moduleFunction !== "function") {
+            inspectedWindow.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
+            return null;
+        }
+        var module = moduleFunction.call(inspectedWindow, InjectedScriptHost, inspectedWindow, injectedScriptId);
         this._modules[name] = module;
         return module;
     },

Modified: trunk/Source/WebCore/inspector/InjectedScriptWebGLModuleSource.js (126282 => 126283)


--- trunk/Source/WebCore/inspector/InjectedScriptWebGLModuleSource.js	2012-08-22 09:10:23 UTC (rev 126282)
+++ trunk/Source/WebCore/inspector/InjectedScriptWebGLModuleSource.js	2012-08-22 09:19:06 UTC (rev 126283)
@@ -30,104 +30,450 @@
 
 /**
  * @param {InjectedScriptHost} InjectedScriptHost
+ * @param {Window} inspectedWindow
+ * @param {number} injectedScriptId
  */
 (function (InjectedScriptHost, inspectedWindow, injectedScriptId) {
 
 /**
  * @constructor
  */
-var InjectedScript = function()
+function Cache()
 {
-    this._lastBoundObjectId = 0;
-    this._idToWrapperProxy = {};
-    this._idToRealWebGLContext = {};
-    this._capturingFrameInfo = null;
+    this.reset();
 }
 
-InjectedScript.prototype = {
-    wrapWebGLContext: function(glContext)
+Cache.prototype = {
+    /**
+     * @return {number}
+     */
+    size: function()
     {
-        for (var id in this._idToRealWebGLContext) {
-            if (this._idToRealWebGLContext[id] === glContext)
-                return this._idToWrapperProxy[id];
-        }
+        return this._size;
+    },
 
-        var proxy = {};
-        var nameProcessed = {};
-        nameProcessed.__proto__ = null;
-        nameProcessed.constructor = true;
+    reset: function()
+    {
+        this._items = Object.create(null);
+        this._size = 0;
+    },
 
-        function processName(name) {
-            if (nameProcessed[name])
-                return;
-            nameProcessed[name] = true;
-            if (typeof glContext[name] === "function")
-                proxy[name] = injectedScript._wrappedFunction.bind(injectedScript, glContext, name);
-            else
-                Object.defineProperty(proxy, name, {
+    /**
+     * @param {number} key
+     * @return {boolean}
+     */
+    has: function(key)
+    {
+        return key in this._items;
+    },
+
+    /**
+     * @param {number} key
+     * @return {Object}
+     */
+    get: function(key)
+    {
+        return this._items[key];
+    },
+
+    /**
+     * @param {number} key
+     * @param {Object} item
+     */
+    put: function(key, item)
+    {
+        if (!this.has(key))
+            ++this._size;
+        this._items[key] = item;
+    }
+}
+
+/**
+ * @constructor
+ * @param {Resource|Object} thisObject
+ * @param {string} functionName
+ * @param {Array|Arguments} args
+ * @param {Resource|*} result
+ */
+function Call(thisObject, functionName, args, result)
+{
+    this._thisObject = thisObject;
+    this._functionName = functionName;
+    this._args = Array.prototype.slice.call(args, 0);
+    this._result = result;
+}
+
+Call.prototype = {
+    /**
+     * @return {Resource}
+     */
+    resource: function()
+    {
+        return Resource.forObject(this._thisObject);
+    },
+
+    /**
+     * @return {string}
+     */
+    functionName: function()
+    {
+        return this._functionName;
+    },
+
+    /**
+     * @return {Array}
+     */
+    args: function()
+    {
+        return this._args;
+    },
+
+    /**
+     * @return {*}
+     */
+    result: function()
+    {
+        return this._result;
+    }
+}
+
+/**
+ * @constructor
+ * @param {Object} wrappedObject
+ */
+function Resource(wrappedObject)
+{
+    this._id = ++Resource._uniqueId;
+    this._resourceManager = null;
+    this.setWrappedObject(wrappedObject);
+}
+
+Resource._uniqueId = 0;
+
+/**
+ * @param {Object} obj
+ * @return {Resource}
+ */
+Resource.forObject = function(obj)
+{
+    if (!obj || obj instanceof Resource)
+        return obj;
+    if (typeof obj === "object")
+        return obj["__resourceObject"];
+    return null;
+}
+
+Resource.prototype = {
+    /**
+     * @return {number}
+     */
+    id: function()
+    {
+        return this._id;
+    },
+
+    /**
+     * @return {Object}
+     */
+    wrappedObject: function()
+    {
+        return this._wrappedObject;
+    },
+
+    /**
+     * @param {Object} value
+     */
+    setWrappedObject: function(value)
+    {
+        console.assert(value && !(value instanceof Resource), "Binding a Resource object to another Resource object?");
+        this._wrappedObject = value;
+        this._bindObjectToResource(value);
+    },
+
+    /**
+     * @return {Object}
+     */
+    proxyObject: function()
+    {
+        // No proxy wrapping by default.
+        return this.wrappedObject();
+    },
+
+    /**
+     * @return {ResourceTrackingManager}
+     */
+    manager: function()
+    {
+        return this._resourceManager;
+    },
+
+    /**
+     * @param {ResourceTrackingManager} value
+     */
+    setManager: function(value)
+    {
+        this._resourceManager = value;
+    },
+
+    /**
+     * @param {Object} object
+     */
+    _bindObjectToResource: function(object)
+    {
+        object["__resourceObject"] = this;
+    }
+}
+
+/**
+ * @constructor
+ * @extends {Resource}
+ * @param {WebGLRenderingContext} glContext
+ */
+function WebGLRenderingContextResource(glContext)
+{
+    Resource.call(this, glContext);
+    this._proxyObject = null;
+}
+
+WebGLRenderingContextResource.prototype = {
+    /**
+     * @return {Object}
+     */
+    proxyObject: function()
+    {
+        if (!this._proxyObject)
+            this._proxyObject = this._wrapObject();
+        return this._proxyObject;
+    },
+
+    /**
+     * @return {Object}
+     */
+    _wrapObject: function()
+    {
+        var glContext = this.wrappedObject();
+        var proxy = Object.create(glContext.__proto__); // In order to emulate "instanceof".
+
+        var self = this;
+        function processProperty(property)
+        {
+            if (typeof glContext[property] === "function") {
+                // FIXME: override GL calls affecting resources states here.
+                proxy[property] = self._wrapFunction(self, glContext, glContext[property], property);
+            } else if (/^[A-Z0-9_]+$/.test(property)) {
+                // Fast access to enums and constants.
+                proxy[property] = glContext[property];
+            } else {
+                Object.defineProperty(proxy, property, {
                     get: function()
                     {
-                        return glContext[name];
+                        return glContext[property];
                     },
                     set: function(value)
                     {
-                        glContext[name] = value;
+                        glContext[property] = value;
                     }
                 });
+            }
         }
 
-        for (var o = glContext; o; o = o.__proto__)
-            Object.getOwnPropertyNames(o).forEach(processName);
+        for (var property in glContext)
+            processProperty(property);
 
-        // In order to emulate "instanceof".
-        proxy.__proto__ = glContext.__proto__;
-        proxy.constructor = glContext.constructor;
+        return proxy;
+    },
 
-        var contextId = this._generateObjectId();
-        this._idToWrapperProxy[contextId] = proxy;
-        this._idToRealWebGLContext[contextId] = glContext;
-        InjectedScriptHost.webGLContextCreated(contextId);
+    /**
+     * @param {Resource} resource
+     * @param {WebGLRenderingContext} originalObject
+     * @param {Function} originalFunction
+     * @param {string} functionName
+     * @return {*}
+     */
+    _wrapFunction: function(resource, originalObject, originalFunction, functionName)
+    {
+        return function()
+        {
+            var manager = resource.manager();
+            if (!manager || !manager.capturing())
+                return originalFunction.apply(originalObject, arguments);
+            manager.captureArguments(resource, arguments);
+            var result = originalFunction.apply(originalObject, arguments);
+            var call = new Call(resource, functionName, arguments, result);
+            manager.reportCall(call);
+            return result;
+        };
+    }
+}
 
-        return proxy;
+WebGLRenderingContextResource.prototype.__proto__ = Resource.prototype;
+
+/**
+ * @constructor
+ * @param {WebGLRenderingContext} originalObject
+ * @param {Function} originalFunction
+ * @param {string} functionName
+ * @param {Array} args
+ */
+WebGLRenderingContextResource.WrapFunction = function(originalObject, originalFunction, functionName, args)
+{
+    this._originalObject = originalObject;
+    this._originalFunction = originalFunction;
+    this._functionName = functionName;
+    this._args = args;
+    this._glResource = Resource.forObject(originalObject);
+}
+
+WebGLRenderingContextResource.WrapFunction.prototype = {
+    /**
+     * @return {*}
+     */
+    result: function()
+    {
+        if (!this._executed) {
+            this._executed = true;
+            this._result = this._originalFunction.apply(this._originalObject, this._args);
+        }
+        return this._result;
     },
 
-    _generateObjectId: function()
+    /**
+     * @return {Call}
+     */
+    call: function()
     {
-        var id = ++this._lastBoundObjectId;
-        var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"webGLId\":" + id + "}";
-        return objectId;
+        if (!this._call)
+            this._call = new Call(this._glResource, this._functionName, this._args, this.result());
+        return this._call;
+    }
+}
+
+/**
+ * @constructor
+ */
+function TraceLog()
+{
+    this._calls = [];
+    this._resourceCache = new Cache();
+}
+
+TraceLog.prototype = {
+    /**
+     * @return {number}
+     */
+    size: function()
+    {
+        return this._calls.length;
     },
 
-    captureFrame: function(contextId)
+    /**
+     * @param {Resource} resource
+     */
+    captureResource: function(resource)
     {
-        this._capturingFrameInfo = {
-            contextId: contextId,
-            capturedCallsNum: 0
-        };
+        // FIXME: Capture current resource state to start the replay from.
     },
 
-    _stopCapturing: function(info)
+    /**
+     * @param {Call} call
+     */
+    addCall: function(call)
     {
-        if (this._capturingFrameInfo === info)
-            this._capturingFrameInfo = null;
+        // FIXME: Clone call and push the clone.
+        this._calls.push(call);
+    }
+}
+
+/**
+ * @constructor
+ */
+function ResourceTrackingManager()
+{
+    this._capturing = false;
+    this._stopCapturingOnFrameEnd = false;
+    this._lastTraceLog = null;
+}
+
+ResourceTrackingManager.prototype = {
+    /**
+     * @return {boolean}
+     */
+    capturing: function()
+    {
+        return this._capturing;
     },
 
-    _wrappedFunction: function(glContext, functionName)
+    /**
+     * @return {TraceLog}
+     */
+    lastTraceLog: function()
     {
-        // Call real WebGL function.
-        var args = Array.prototype.slice.call(arguments, 2);
-        var result = glContext[functionName].apply(glContext, args);
+        return this._lastTraceLog;
+    },
 
-        if (this._capturingFrameInfo && this._idToRealWebGLContext[this._capturingFrameInfo.contextId] === glContext) {
-            var capturedCallsNum = ++this._capturingFrameInfo.capturedCallsNum;
-            if (capturedCallsNum === 1)
-                this._setZeroTimeouts(this._stopCapturing.bind(this, this._capturingFrameInfo));
-            InjectedScriptHost.webGLReportFunctionCall(this._capturingFrameInfo.contextId, functionName, "[" + args.join(", ") + "]", result + "");
+    /**
+     * @param {Resource} resource
+     */
+    registerResource: function(resource)
+    {
+        resource.setManager(this);
+    },
+
+    startCapturing: function()
+    {
+        if (!this._capturing)
+            this._lastTraceLog = new TraceLog();
+        this._capturing = true;
+        this._stopCapturingOnFrameEnd = false;
+    },
+
+    /**
+     * @param {TraceLog=} traceLog
+     */
+    stopCapturing: function(traceLog)
+    {
+        if (traceLog && this._lastTraceLog !== traceLog)
+            return;
+        this._capturing = false;
+        this._stopCapturingOnFrameEnd = false;
+    },
+
+    captureFrame: function()
+    {
+        this._lastTraceLog = new TraceLog();
+        this._capturing = true;
+        this._stopCapturingOnFrameEnd = true;
+    },
+
+    captureArguments: function(resource, args)
+    {
+        if (!this._capturing)
+            return;
+        this._lastTraceLog.captureResource(resource);
+        for (var i = 0, n = args.length; i < n; ++i) {
+            var res = Resource.forObject(args[i]);
+            if (res)
+                this._lastTraceLog.captureResource(res);
         }
+    },
 
-        return result;
+    /**
+     * @param {Call} call
+     */
+    reportCall: function(call)
+    {
+        if (!this._capturing)
+            return;
+        this._lastTraceLog.addCall(call);
+        if (this._stopCapturingOnFrameEnd && this._lastTraceLog.size() === 1) {
+            this._stopCapturingOnFrameEnd = false;
+            this._setZeroTimeouts(this.stopCapturing.bind(this, this._lastTraceLog));
+        }
     },
 
+    /**
+     * @param {Function} callback
+     */
     _setZeroTimeouts: function(callback)
     {
         // We need a fastest async callback, whatever fires first.
@@ -137,8 +483,35 @@
         channel.port2.postMessage("");
         inspectedWindow.setTimeout(callback, 0);
     }
-};
+}
 
+/**
+ * @constructor
+ */
+var InjectedScript = function()
+{
+    this._manager = new ResourceTrackingManager();
+}
+
+InjectedScript.prototype = {
+    /**
+     * @param {WebGLRenderingContext} glContext
+     * @return {Object}
+     */
+    wrapWebGLContext: function(glContext)
+    {
+        var resource = Resource.forObject(glContext) || new WebGLRenderingContextResource(glContext);
+        this._manager.registerResource(resource);
+        var proxy = resource.proxyObject();
+        return proxy;
+    },
+
+    captureFrame: function()
+    {
+        this._manager.captureFrame();
+    }
+}
+
 var injectedScript = new InjectedScript();
 return injectedScript;
 

Modified: trunk/Source/WebCore/inspector/compile-front-end.py (126282 => 126283)


--- trunk/Source/WebCore/inspector/compile-front-end.py	2012-08-22 09:10:23 UTC (rev 126282)
+++ trunk/Source/WebCore/inspector/compile-front-end.py	2012-08-22 09:19:06 UTC (rev 126283)
@@ -377,3 +377,14 @@
 command += "\n"
 os.system(command)
 os.system("rm " + inspector_path + "/" + "InjectedScriptSourceTmp.js")
+
+print "Compiling InjectedScriptWebGLModuleSource.js..."
+os.system("echo \"var injectedScriptWebGLModuleValue = \" > " + inspector_path + "/" + "InjectedScriptWebGLModuleSourceTmp.js")
+os.system("cat  " + inspector_path + "/" + "InjectedScriptWebGLModuleSource.js" + " >> " + inspector_path + "/" + "InjectedScriptWebGLModuleSourceTmp.js")
+command = compiler_command
+command += "    --externs " + inspector_path + "/" + "InjectedScriptExterns.js" + " \\\n"
+command += "    --module " + jsmodule_name_prefix + "injected_script" + ":" + "1" + " \\\n"
+command += "        --js " + inspector_path + "/" + "InjectedScriptWebGLModuleSourceTmp.js" + " \\\n"
+command += "\n"
+os.system(command)
+os.system("rm " + inspector_path + "/" + "InjectedScriptWebGLModuleSourceTmp.js")
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to