Changeset 126283 in webkit


Ignore:
Timestamp:
Aug 22, 2012 2:19:06 AM (12 years ago)
Author:
caseq@chromium.org
Message:

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

Patch by Andrey Adaikin <aandrey@chromium.org> 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:

(.):

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r126280 r126283  
     12012-08-22  Andrey Adaikin  <aandrey@chromium.org>
     2
     3        Web Inspector: [WebGL] Generic framework draft for tracking WebGL resources
     4        https://bugs.webkit.org/show_bug.cgi?id=90597
     5
     6        Reviewed by Pavel Feldman.
     7
     8        Wrap WebGL rendering context methods and collect a trace log if we are in capturing mode.
     9        Stubbed code for collecting calls contributing to a WebGL resource state so that we could replay them later.
     10
     11        Typical scenario:
     12        - we wrap a GL context with InjectedScript.wrapWebGLContext() and return a proxy to the inspected page
     13        - the proxy saves all calls necessary to do a replay later - only those that modify a resource's state
     14        - when we turn on capturing mode (InjectedScript.captureFrame), we save all WebGL calls to a trace log
     15
     16        * inspector/InjectedScriptSource.js:
     17        (.):
     18        * inspector/InjectedScriptWebGLModuleSource.js:
     19        (.):
     20
    1212012-08-22  Andrey Adaikin  <aandrey@chromium.org>
    222
  • trunk/Source/WebCore/inspector/InjectedScriptSource.js

    r126168 r126283  
    555555    },
    556556
     557    /**
     558     * @param {string} name
     559     * @return {Object}
     560     */
    557561    module: function(name)
    558562    {
    559563        return this._modules[name];
    560564    },
    561  
     565
     566    /**
     567     * @param {string} name
     568     * @param {string} source
     569     * @return {Object}
     570     */
    562571    injectModule: function(name, source)
    563572    {
    564573        delete this._modules[name];
    565         var module = InjectedScriptHost.evaluate("(" + source + ")");
     574        var moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
     575        if (typeof moduleFunction !== "function") {
     576            inspectedWindow.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
     577            return null;
     578        }
     579        var module = moduleFunction.call(inspectedWindow, InjectedScriptHost, inspectedWindow, injectedScriptId);
    566580        this._modules[name] = module;
    567581        return module;
  • trunk/Source/WebCore/inspector/InjectedScriptWebGLModuleSource.js

    r120957 r126283  
    3131/**
    3232 * @param {InjectedScriptHost} InjectedScriptHost
     33 * @param {Window} inspectedWindow
     34 * @param {number} injectedScriptId
    3335 */
    3436(function (InjectedScriptHost, inspectedWindow, injectedScriptId) {
     
    3739 * @constructor
    3840 */
    39 var InjectedScript = function()
    40 {
    41     this._lastBoundObjectId = 0;
    42     this._idToWrapperProxy = {};
    43     this._idToRealWebGLContext = {};
    44     this._capturingFrameInfo = null;
    45 }
    46 
    47 InjectedScript.prototype = {
    48     wrapWebGLContext: function(glContext)
    49     {
    50         for (var id in this._idToRealWebGLContext) {
    51             if (this._idToRealWebGLContext[id] === glContext)
    52                 return this._idToWrapperProxy[id];
    53         }
    54 
    55         var proxy = {};
    56         var nameProcessed = {};
    57         nameProcessed.__proto__ = null;
    58         nameProcessed.constructor = true;
    59 
    60         function processName(name) {
    61             if (nameProcessed[name])
    62                 return;
    63             nameProcessed[name] = true;
    64             if (typeof glContext[name] === "function")
    65                 proxy[name] = injectedScript._wrappedFunction.bind(injectedScript, glContext, name);
    66             else
    67                 Object.defineProperty(proxy, name, {
     41function Cache()
     42{
     43    this.reset();
     44}
     45
     46Cache.prototype = {
     47    /**
     48     * @return {number}
     49     */
     50    size: function()
     51    {
     52        return this._size;
     53    },
     54
     55    reset: function()
     56    {
     57        this._items = Object.create(null);
     58        this._size = 0;
     59    },
     60
     61    /**
     62     * @param {number} key
     63     * @return {boolean}
     64     */
     65    has: function(key)
     66    {
     67        return key in this._items;
     68    },
     69
     70    /**
     71     * @param {number} key
     72     * @return {Object}
     73     */
     74    get: function(key)
     75    {
     76        return this._items[key];
     77    },
     78
     79    /**
     80     * @param {number} key
     81     * @param {Object} item
     82     */
     83    put: function(key, item)
     84    {
     85        if (!this.has(key))
     86            ++this._size;
     87        this._items[key] = item;
     88    }
     89}
     90
     91/**
     92 * @constructor
     93 * @param {Resource|Object} thisObject
     94 * @param {string} functionName
     95 * @param {Array|Arguments} args
     96 * @param {Resource|*} result
     97 */
     98function Call(thisObject, functionName, args, result)
     99{
     100    this._thisObject = thisObject;
     101    this._functionName = functionName;
     102    this._args = Array.prototype.slice.call(args, 0);
     103    this._result = result;
     104}
     105
     106Call.prototype = {
     107    /**
     108     * @return {Resource}
     109     */
     110    resource: function()
     111    {
     112        return Resource.forObject(this._thisObject);
     113    },
     114
     115    /**
     116     * @return {string}
     117     */
     118    functionName: function()
     119    {
     120        return this._functionName;
     121    },
     122
     123    /**
     124     * @return {Array}
     125     */
     126    args: function()
     127    {
     128        return this._args;
     129    },
     130
     131    /**
     132     * @return {*}
     133     */
     134    result: function()
     135    {
     136        return this._result;
     137    }
     138}
     139
     140/**
     141 * @constructor
     142 * @param {Object} wrappedObject
     143 */
     144function Resource(wrappedObject)
     145{
     146    this._id = ++Resource._uniqueId;
     147    this._resourceManager = null;
     148    this.setWrappedObject(wrappedObject);
     149}
     150
     151Resource._uniqueId = 0;
     152
     153/**
     154 * @param {Object} obj
     155 * @return {Resource}
     156 */
     157Resource.forObject = function(obj)
     158{
     159    if (!obj || obj instanceof Resource)
     160        return obj;
     161    if (typeof obj === "object")
     162        return obj["__resourceObject"];
     163    return null;
     164}
     165
     166Resource.prototype = {
     167    /**
     168     * @return {number}
     169     */
     170    id: function()
     171    {
     172        return this._id;
     173    },
     174
     175    /**
     176     * @return {Object}
     177     */
     178    wrappedObject: function()
     179    {
     180        return this._wrappedObject;
     181    },
     182
     183    /**
     184     * @param {Object} value
     185     */
     186    setWrappedObject: function(value)
     187    {
     188        console.assert(value && !(value instanceof Resource), "Binding a Resource object to another Resource object?");
     189        this._wrappedObject = value;
     190        this._bindObjectToResource(value);
     191    },
     192
     193    /**
     194     * @return {Object}
     195     */
     196    proxyObject: function()
     197    {
     198        // No proxy wrapping by default.
     199        return this.wrappedObject();
     200    },
     201
     202    /**
     203     * @return {ResourceTrackingManager}
     204     */
     205    manager: function()
     206    {
     207        return this._resourceManager;
     208    },
     209
     210    /**
     211     * @param {ResourceTrackingManager} value
     212     */
     213    setManager: function(value)
     214    {
     215        this._resourceManager = value;
     216    },
     217
     218    /**
     219     * @param {Object} object
     220     */
     221    _bindObjectToResource: function(object)
     222    {
     223        object["__resourceObject"] = this;
     224    }
     225}
     226
     227/**
     228 * @constructor
     229 * @extends {Resource}
     230 * @param {WebGLRenderingContext} glContext
     231 */
     232function WebGLRenderingContextResource(glContext)
     233{
     234    Resource.call(this, glContext);
     235    this._proxyObject = null;
     236}
     237
     238WebGLRenderingContextResource.prototype = {
     239    /**
     240     * @return {Object}
     241     */
     242    proxyObject: function()
     243    {
     244        if (!this._proxyObject)
     245            this._proxyObject = this._wrapObject();
     246        return this._proxyObject;
     247    },
     248
     249    /**
     250     * @return {Object}
     251     */
     252    _wrapObject: function()
     253    {
     254        var glContext = this.wrappedObject();
     255        var proxy = Object.create(glContext.__proto__); // In order to emulate "instanceof".
     256
     257        var self = this;
     258        function processProperty(property)
     259        {
     260            if (typeof glContext[property] === "function") {
     261                // FIXME: override GL calls affecting resources states here.
     262                proxy[property] = self._wrapFunction(self, glContext, glContext[property], property);
     263            } else if (/^[A-Z0-9_]+$/.test(property)) {
     264                // Fast access to enums and constants.
     265                proxy[property] = glContext[property];
     266            } else {
     267                Object.defineProperty(proxy, property, {
    68268                    get: function()
    69269                    {
    70                         return glContext[name];
     270                        return glContext[property];
    71271                    },
    72272                    set: function(value)
    73273                    {
    74                         glContext[name] = value;
     274                        glContext[property] = value;
    75275                    }
    76276                });
     277            }
    77278        }
    78279
    79         for (var o = glContext; o; o = o.__proto__)
    80             Object.getOwnPropertyNames(o).forEach(processName);
    81 
    82         // In order to emulate "instanceof".
    83         proxy.__proto__ = glContext.__proto__;
    84         proxy.constructor = glContext.constructor;
    85 
    86         var contextId = this._generateObjectId();
    87         this._idToWrapperProxy[contextId] = proxy;
    88         this._idToRealWebGLContext[contextId] = glContext;
    89         InjectedScriptHost.webGLContextCreated(contextId);
     280        for (var property in glContext)
     281            processProperty(property);
    90282
    91283        return proxy;
    92284    },
    93285
    94     _generateObjectId: function()
    95     {
    96         var id = ++this._lastBoundObjectId;
    97         var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"webGLId\":" + id + "}";
    98         return objectId;
    99     },
    100 
    101     captureFrame: function(contextId)
    102     {
    103         this._capturingFrameInfo = {
    104             contextId: contextId,
    105             capturedCallsNum: 0
     286    /**
     287     * @param {Resource} resource
     288     * @param {WebGLRenderingContext} originalObject
     289     * @param {Function} originalFunction
     290     * @param {string} functionName
     291     * @return {*}
     292     */
     293    _wrapFunction: function(resource, originalObject, originalFunction, functionName)
     294    {
     295        return function()
     296        {
     297            var manager = resource.manager();
     298            if (!manager || !manager.capturing())
     299                return originalFunction.apply(originalObject, arguments);
     300            manager.captureArguments(resource, arguments);
     301            var result = originalFunction.apply(originalObject, arguments);
     302            var call = new Call(resource, functionName, arguments, result);
     303            manager.reportCall(call);
     304            return result;
    106305        };
    107     },
    108 
    109     _stopCapturing: function(info)
    110     {
    111         if (this._capturingFrameInfo === info)
    112             this._capturingFrameInfo = null;
    113     },
    114 
    115     _wrappedFunction: function(glContext, functionName)
    116     {
    117         // Call real WebGL function.
    118         var args = Array.prototype.slice.call(arguments, 2);
    119         var result = glContext[functionName].apply(glContext, args);
    120 
    121         if (this._capturingFrameInfo && this._idToRealWebGLContext[this._capturingFrameInfo.contextId] === glContext) {
    122             var capturedCallsNum = ++this._capturingFrameInfo.capturedCallsNum;
    123             if (capturedCallsNum === 1)
    124                 this._setZeroTimeouts(this._stopCapturing.bind(this, this._capturingFrameInfo));
    125             InjectedScriptHost.webGLReportFunctionCall(this._capturingFrameInfo.contextId, functionName, "[" + args.join(", ") + "]", result + "");
     306    }
     307}
     308
     309WebGLRenderingContextResource.prototype.__proto__ = Resource.prototype;
     310
     311/**
     312 * @constructor
     313 * @param {WebGLRenderingContext} originalObject
     314 * @param {Function} originalFunction
     315 * @param {string} functionName
     316 * @param {Array} args
     317 */
     318WebGLRenderingContextResource.WrapFunction = function(originalObject, originalFunction, functionName, args)
     319{
     320    this._originalObject = originalObject;
     321    this._originalFunction = originalFunction;
     322    this._functionName = functionName;
     323    this._args = args;
     324    this._glResource = Resource.forObject(originalObject);
     325}
     326
     327WebGLRenderingContextResource.WrapFunction.prototype = {
     328    /**
     329     * @return {*}
     330     */
     331    result: function()
     332    {
     333        if (!this._executed) {
     334            this._executed = true;
     335            this._result = this._originalFunction.apply(this._originalObject, this._args);
    126336        }
    127 
    128         return result;
    129     },
    130 
     337        return this._result;
     338    },
     339
     340    /**
     341     * @return {Call}
     342     */
     343    call: function()
     344    {
     345        if (!this._call)
     346            this._call = new Call(this._glResource, this._functionName, this._args, this.result());
     347        return this._call;
     348    }
     349}
     350
     351/**
     352 * @constructor
     353 */
     354function TraceLog()
     355{
     356    this._calls = [];
     357    this._resourceCache = new Cache();
     358}
     359
     360TraceLog.prototype = {
     361    /**
     362     * @return {number}
     363     */
     364    size: function()
     365    {
     366        return this._calls.length;
     367    },
     368
     369    /**
     370     * @param {Resource} resource
     371     */
     372    captureResource: function(resource)
     373    {
     374        // FIXME: Capture current resource state to start the replay from.
     375    },
     376
     377    /**
     378     * @param {Call} call
     379     */
     380    addCall: function(call)
     381    {
     382        // FIXME: Clone call and push the clone.
     383        this._calls.push(call);
     384    }
     385}
     386
     387/**
     388 * @constructor
     389 */
     390function ResourceTrackingManager()
     391{
     392    this._capturing = false;
     393    this._stopCapturingOnFrameEnd = false;
     394    this._lastTraceLog = null;
     395}
     396
     397ResourceTrackingManager.prototype = {
     398    /**
     399     * @return {boolean}
     400     */
     401    capturing: function()
     402    {
     403        return this._capturing;
     404    },
     405
     406    /**
     407     * @return {TraceLog}
     408     */
     409    lastTraceLog: function()
     410    {
     411        return this._lastTraceLog;
     412    },
     413
     414    /**
     415     * @param {Resource} resource
     416     */
     417    registerResource: function(resource)
     418    {
     419        resource.setManager(this);
     420    },
     421
     422    startCapturing: function()
     423    {
     424        if (!this._capturing)
     425            this._lastTraceLog = new TraceLog();
     426        this._capturing = true;
     427        this._stopCapturingOnFrameEnd = false;
     428    },
     429
     430    /**
     431     * @param {TraceLog=} traceLog
     432     */
     433    stopCapturing: function(traceLog)
     434    {
     435        if (traceLog && this._lastTraceLog !== traceLog)
     436            return;
     437        this._capturing = false;
     438        this._stopCapturingOnFrameEnd = false;
     439    },
     440
     441    captureFrame: function()
     442    {
     443        this._lastTraceLog = new TraceLog();
     444        this._capturing = true;
     445        this._stopCapturingOnFrameEnd = true;
     446    },
     447
     448    captureArguments: function(resource, args)
     449    {
     450        if (!this._capturing)
     451            return;
     452        this._lastTraceLog.captureResource(resource);
     453        for (var i = 0, n = args.length; i < n; ++i) {
     454            var res = Resource.forObject(args[i]);
     455            if (res)
     456                this._lastTraceLog.captureResource(res);
     457        }
     458    },
     459
     460    /**
     461     * @param {Call} call
     462     */
     463    reportCall: function(call)
     464    {
     465        if (!this._capturing)
     466            return;
     467        this._lastTraceLog.addCall(call);
     468        if (this._stopCapturingOnFrameEnd && this._lastTraceLog.size() === 1) {
     469            this._stopCapturingOnFrameEnd = false;
     470            this._setZeroTimeouts(this.stopCapturing.bind(this, this._lastTraceLog));
     471        }
     472    },
     473
     474    /**
     475     * @param {Function} callback
     476     */
    131477    _setZeroTimeouts: function(callback)
    132478    {
     
    138484        inspectedWindow.setTimeout(callback, 0);
    139485    }
    140 };
     486}
     487
     488/**
     489 * @constructor
     490 */
     491var InjectedScript = function()
     492{
     493    this._manager = new ResourceTrackingManager();
     494}
     495
     496InjectedScript.prototype = {
     497    /**
     498     * @param {WebGLRenderingContext} glContext
     499     * @return {Object}
     500     */
     501    wrapWebGLContext: function(glContext)
     502    {
     503        var resource = Resource.forObject(glContext) || new WebGLRenderingContextResource(glContext);
     504        this._manager.registerResource(resource);
     505        var proxy = resource.proxyObject();
     506        return proxy;
     507    },
     508
     509    captureFrame: function()
     510    {
     511        this._manager.captureFrame();
     512    }
     513}
    141514
    142515var injectedScript = new InjectedScript();
  • trunk/Source/WebCore/inspector/compile-front-end.py

    r126012 r126283  
    378378os.system(command)
    379379os.system("rm " + inspector_path + "/" + "InjectedScriptSourceTmp.js")
     380
     381print "Compiling InjectedScriptWebGLModuleSource.js..."
     382os.system("echo \"var injectedScriptWebGLModuleValue = \" > " + inspector_path + "/" + "InjectedScriptWebGLModuleSourceTmp.js")
     383os.system("cat  " + inspector_path + "/" + "InjectedScriptWebGLModuleSource.js" + " >> " + inspector_path + "/" + "InjectedScriptWebGLModuleSourceTmp.js")
     384command = compiler_command
     385command += "    --externs " + inspector_path + "/" + "InjectedScriptExterns.js" + " \\\n"
     386command += "    --module " + jsmodule_name_prefix + "injected_script" + ":" + "1" + " \\\n"
     387command += "        --js " + inspector_path + "/" + "InjectedScriptWebGLModuleSourceTmp.js" + " \\\n"
     388command += "\n"
     389os.system(command)
     390os.system("rm " + inspector_path + "/" + "InjectedScriptWebGLModuleSourceTmp.js")
Note: See TracChangeset for help on using the changeset viewer.