Changeset 126747 in webkit


Ignore:
Timestamp:
Aug 27, 2012 4:38:44 AM (12 years ago)
Author:
caseq@chromium.org
Message:

Web Inspector: [WebGL] Implement serializing WebGL state and replaying it later
https://bugs.webkit.org/show_bug.cgi?id=94933

Patch by Andrey Adaikin <aandrey@chromium.org> on 2012-08-27
Reviewed by Pavel Feldman.

This will allow us to save the WebGL state at any arbitrary point in order to replay a trace log later on another canvas.
Main points of the change:

  • Allow every Resource and Call instances be serialized to a Replayable, and resurrected back during a replay on another canvas.
  • Before executing an original WebGL call and saving it to a trace log, first serialize all the Resources that will be involved in this call, if they are not yet serialized. The latter part is implemented with a Cache instance, checking that we convert a Resource to a Replayable only once: the first time it was used in the trace log being collected. We do not need to serialize the subsequent changes to this Resource's state (if any), since they will be saved in the trace log being collected and replayed automatically.
  • Some information about the WebGL state may be requested in the runtime (e.g. with the gl.getParameter method). We use this wherever possible instead of collecting Calls in a Resource log. Otherwise, we have to collect WebGL calls (like gl.texImage2D) in order to replay them later.
  • Later the Replayable objects hierarchy (the TraceLog) may be serialized further for cross-device transmission, for example.
  • inspector/InjectedScriptWebGLModuleSource.js:

(.):

Location:
trunk/Source/WebCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r126746 r126747  
     12012-08-27  Andrey Adaikin  <aandrey@chromium.org>
     2
     3        Web Inspector: [WebGL] Implement serializing WebGL state and replaying it later
     4        https://bugs.webkit.org/show_bug.cgi?id=94933
     5
     6        Reviewed by Pavel Feldman.
     7
     8        This will allow us to save the WebGL state at any arbitrary point in order to replay a trace log later on another canvas.
     9        Main points of the change:
     10        - Allow every Resource and Call instances be serialized to a Replayable, and resurrected back during a replay on another canvas.
     11
     12        - Before executing an original WebGL call and saving it to a trace log, first serialize all the Resources that will be involved in
     13          this call, if they are not yet serialized. The latter part is implemented with a Cache instance, checking that we convert a Resource
     14          to a Replayable only once: the first time it was used in the trace log being collected. We do not need to serialize the subsequent
     15          changes to this Resource's state (if any), since they will be saved in the trace log being collected and replayed automatically.
     16
     17        - Some information about the WebGL state may be requested in the runtime (e.g. with the gl.getParameter method). We use this wherever
     18          possible instead of collecting Calls in a Resource log. Otherwise, we have to collect WebGL calls (like gl.texImage2D) in order to
     19          replay them later.
     20
     21        - Later the Replayable objects hierarchy (the TraceLog) may be serialized further for cross-device transmission, for example.
     22       
     23
     24        * inspector/InjectedScriptWebGLModuleSource.js:
     25        (.):
     26
     272012-08-27  Sheriff Bot  <webkit.review.bot@gmail.com>
     28
     29        Unreviewed, rolling out r126694 and r126731.
     30        http://trac.webkit.org/changeset/126694
     31        http://trac.webkit.org/changeset/126731
     32        https://bugs.webkit.org/show_bug.cgi?id=95069
     33
     34        Causes all pages to be rendered as a completely black page in
     35        Qt WebKit2 (Requested by carewolf on #webkit).
     36
     37        * platform/graphics/GraphicsContext3D.h:
     38        * platform/graphics/texmap/TextureMapperGL.cpp:
     39        (SharedGLData):
     40        (WebCore::TextureMapperGLData::SharedGLData::getCurrentGLContext):
     41        (WebCore::TextureMapperGLData::SharedGLData::currentSharedGLData):
     42        (WebCore::TextureMapperGLData::SharedGLData::SharedGLData):
     43        (WebCore::TextureMapperGLData::sharedGLData):
     44        (WebCore::TextureMapperGLData::TextureMapperGLData):
     45        (TextureMapperGLData):
     46        (WebCore::scissorClip):
     47        (WebCore::TextureMapperGL::ClipStack::apply):
     48        (WebCore::TextureMapperGLData::initializeStencil):
     49        (WebCore::TextureMapperGL::TextureMapperGL):
     50        (WebCore::TextureMapperGL::beginPainting):
     51        (WebCore::TextureMapperGL::endPainting):
     52        (WebCore::TextureMapperGL::drawQuad):
     53        (WebCore::TextureMapperGL::drawBorder):
     54        (WebCore):
     55        (WebCore::TextureMapperGL::drawTextureRectangleARB):
     56        (WebCore::TextureMapperGL::drawTexture):
     57        (WebCore::viewportMatrix):
     58        (WebCore::TextureMapperGL::drawTextureWithAntialiasing):
     59        (WebCore::TextureMapperGL::drawTexturedQuadWithProgram):
     60        (WebCore::BitmapTextureGL::didReset):
     61        (WebCore::BitmapTextureGL::updateContents):
     62        (WebCore::TextureMapperGL::drawFiltered):
     63        (WebCore::BitmapTextureGL::initializeStencil):
     64        (WebCore::BitmapTextureGL::clearIfNeeded):
     65        (WebCore::BitmapTextureGL::createFboIfNeeded):
     66        (WebCore::BitmapTextureGL::bind):
     67        (WebCore::BitmapTextureGL::~BitmapTextureGL):
     68        (WebCore::TextureMapperGL::bindDefaultSurface):
     69        (WebCore::TextureMapperGL::beginScissorClip):
     70        (WebCore::TextureMapperGL::beginClip):
     71        (WebCore::TextureMapperGL::endClip):
     72        (WebCore::TextureMapperGL::createTexture):
     73        * platform/graphics/texmap/TextureMapperGL.h:
     74        (TextureMapperGL):
     75        (ClipStack):
     76        (WebCore::BitmapTextureGL::textureTarget):
     77        (BitmapTextureGL):
     78        (WebCore::BitmapTextureGL::BitmapTextureGL):
     79        * platform/graphics/texmap/TextureMapperShaderManager.cpp:
     80        (WebCore):
     81        (WebCore::TextureMapperShaderManager::getShaderProgram):
     82        (WebCore::TextureMapperShaderProgram::TextureMapperShaderProgram):
     83        (WebCore::TextureMapperShaderProgram::initializeProgram):
     84        (WebCore::TextureMapperShaderProgram::getUniformLocation):
     85        (WebCore::TextureMapperShaderProgram::~TextureMapperShaderProgram):
     86        (WebCore::TextureMapperShaderProgramSimple::TextureMapperShaderProgramSimple):
     87        (WebCore::TextureMapperShaderProgramSolidColor::TextureMapperShaderProgramSolidColor):
     88        (WebCore::TextureMapperShaderProgramRectSimple::TextureMapperShaderProgramRectSimple):
     89        (WebCore::TextureMapperShaderProgramOpacityAndMask::TextureMapperShaderProgramOpacityAndMask):
     90        (WebCore::TextureMapperShaderProgramRectOpacityAndMask::TextureMapperShaderProgramRectOpacityAndMask):
     91        (WebCore::TextureMapperShaderProgramAntialiasingNoMask::TextureMapperShaderProgramAntialiasingNoMask):
     92        (WebCore::TextureMapperShaderManager::TextureMapperShaderManager):
     93        (WebCore::StandardFilterProgram::~StandardFilterProgram):
     94        (WebCore::StandardFilterProgram::StandardFilterProgram):
     95        (WebCore::StandardFilterProgram::create):
     96        (WebCore::StandardFilterProgram::prepare):
     97        (WebCore::TextureMapperShaderManager::getShaderForFilter):
     98        * platform/graphics/texmap/TextureMapperShaderManager.h:
     99        (WebCore::TextureMapperShaderProgram::id):
     100        (WebCore::TextureMapperShaderProgram::vertexAttrib):
     101        (TextureMapperShaderProgram):
     102        (WebCore::TextureMapperShaderProgram::matrixLocation):
     103        (WebCore::TextureMapperShaderProgram::flipLocation):
     104        (WebCore::TextureMapperShaderProgram::textureSizeLocation):
     105        (WebCore::TextureMapperShaderProgram::sourceTextureLocation):
     106        (WebCore::TextureMapperShaderProgram::maskTextureLocation):
     107        (WebCore::TextureMapperShaderProgram::opacityLocation):
     108        (WebCore::TextureMapperShaderProgram::isValidUniformLocation):
     109        (StandardFilterProgram):
     110        (WebCore::StandardFilterProgram::vertexAttrib):
     111        (WebCore::StandardFilterProgram::texCoordAttrib):
     112        (WebCore::StandardFilterProgram::textureUniform):
     113        (WebCore::TextureMapperShaderProgramSimple::create):
     114        (TextureMapperShaderProgramSimple):
     115        (WebCore::TextureMapperShaderProgramRectSimple::create):
     116        (WebCore::TextureMapperShaderProgramOpacityAndMask::create):
     117        (WebCore::TextureMapperShaderProgramRectOpacityAndMask::create):
     118        (WebCore::TextureMapperShaderProgramSolidColor::create):
     119        (WebCore::TextureMapperShaderProgramSolidColor::colorLocation):
     120        (TextureMapperShaderProgramSolidColor):
     121        (WebCore::TextureMapperShaderProgramAntialiasingNoMask::create):
     122        (WebCore::TextureMapperShaderProgramAntialiasingNoMask::expandedQuadVerticesInTextureCoordinatesLocation):
     123        (WebCore::TextureMapperShaderProgramAntialiasingNoMask::expandedQuadEdgesInScreenSpaceLocation):
     124        (TextureMapperShaderProgramAntialiasingNoMask):
     125
    11262012-08-27  Ilya Tikhonovsky  <loislo@chromium.org>
    2127
  • trunk/Source/WebCore/inspector/InjectedScriptWebGLModuleSource.js

    r126585 r126747  
    246246                this._args[i] = TypeUtils.clone(this._args[i]);
    247247        }
     248    },
     249
     250    /**
     251     * @param {Cache} cache
     252     * @return {ReplayableCall}
     253     */
     254    toReplayable: function(cache)
     255    {
     256        this.freeze();
     257        var thisObject = /** @type {ReplayableResource} */ Resource.toReplayable(this._thisObject, cache);
     258        var result = Resource.toReplayable(this._result, cache);
     259        var args = this._args.map(function(obj) {
     260            return Resource.toReplayable(obj, cache);
     261        });
     262        return new ReplayableCall(thisObject, this._functionName, args, result);
     263    },
     264
     265    /**
     266     * @param {ReplayableCall} replayableCall
     267     * @param {Cache} cache
     268     * @return {Call}
     269     */
     270    replay: function(replayableCall, cache)
     271    {
     272        var replayObject = ReplayableResource.replay(replayableCall.resource(), cache);
     273        var replayFunction = replayObject[replayableCall.functionName()];
     274        console.assert(typeof replayFunction === "function", "Expected a function to replay");
     275        var replayArgs = replayableCall.args().map(function(obj) {
     276            return ReplayableResource.replay(obj, cache);
     277        });
     278        var replayResult = replayFunction.apply(replayObject, replayArgs);
     279        if (replayableCall.result() instanceof ReplayableResource) {
     280            var resource = replayableCall.result().replay(cache);
     281            if (!resource.wrappedObject())
     282                resource.setWrappedObject(replayResult);
     283        }
     284
     285        this._thisObject = replayObject;
     286        this._functionName = replayableCall.functionName();
     287        this._args = replayArgs;
     288        this._result = replayResult;
     289        this._freezed = true;
     290        return this;
    248291    }
    249292}
     
    303346    replay: function(cache)
    304347    {
    305         // FIXME: Do the replay.
     348        var call = Object.create(Call.prototype);
     349        return call.replay(this, cache);
    306350    }
    307351}
     
    339383}
    340384
     385/**
     386 * @param {Resource|*} obj
     387 * @param {Cache} cache
     388 * @return {ReplayableResource|*}
     389 */
     390Resource.toReplayable = function(obj, cache)
     391{
     392    var resource = Resource.forObject(obj);
     393    return resource ? resource.toReplayable(cache) : obj;
     394}
     395
    341396Resource.prototype = {
    342397    /**
     
    401456
    402457    /**
     458     * @param {Cache} cache
     459     * @return {ReplayableResource}
     460     */
     461    toReplayable: function(cache)
     462    {
     463        var result = cache.get(this._id);
     464        if (result)
     465            return result;
     466        var data = {
     467            id: this._id
     468        };
     469        result = new ReplayableResource(this, data);
     470        cache.put(this._id, result); // Put into the cache early to avoid loops.
     471        data.calls = this._calls.map(function(call) {
     472            return call.toReplayable(cache);
     473        });
     474        this._populateReplayableData(data, cache);
     475        return result;
     476    },
     477
     478    /**
     479     * @param {Object} data
     480     * @param {Cache} cache
     481     */
     482    _populateReplayableData: function(data, cache)
     483    {
     484        // Do nothing. Should be overridden by subclasses.
     485    },
     486
     487    /**
     488     * @param {Object} data
     489     * @param {Cache} cache
     490     * @return {Resource}
     491     */
     492    replay: function(data, cache)
     493    {
     494        var resource = cache.get(data.id);
     495        if (resource)
     496            return resource;
     497        this._id = data.id;
     498        this._resourceManager = null;
     499        this._calls = [];
     500        this._wrappedObject = null;
     501        cache.put(data.id, this); // Put into the cache early to avoid loops.
     502        this._doReplayCalls(data, cache);
     503        console.assert(this._wrappedObject, "Resource should be reconstructed!");
     504        return this;
     505    },
     506
     507    /**
     508     * @param {Object} data
     509     * @param {Cache} cache
     510     */
     511    _doReplayCalls: function(data, cache)
     512    {
     513        for (var i = 0, n = data.calls.length; i < n; ++i)
     514            this._calls.push(data.calls[i].replay(cache));
     515    },
     516
     517    /**
    403518     * @param {Call} call
    404519     */
     
    425540function ReplayableResource(originalResource, data)
    426541{
     542    this._proto = originalResource.__proto__;
     543    this._data = data;
    427544}
    428545
     
    434551    replay: function(cache)
    435552    {
    436         // FIXME: Do the replay.
    437     }
     553        var result = Object.create(this._proto);
     554        result = result.replay(this._data, cache)
     555        console.assert(result.__proto__ === this._proto, "Wrong type of a replay result");
     556        return result;
     557    }
     558}
     559
     560/**
     561 * @param {ReplayableResource|*} obj
     562 * @param {Cache} cache
     563 * @return {*}
     564 */
     565ReplayableResource.replay = function(obj, cache)
     566{
     567    return (obj instanceof ReplayableResource) ? obj.replay(cache).wrappedObject() : obj;
    438568}
    439569
     
    449579
    450580WebGLBoundResource.prototype = {
     581    /**
     582     * @override
     583     * @param {Object} data
     584     * @param {Cache} cache
     585     */
     586    _populateReplayableData: function(data, cache)
     587    {
     588        var state = this._state;
     589        data.state = {};
     590        Object.keys(state).forEach(function(parameter) {
     591            data.state[parameter] = Resource.toReplayable(state[parameter], cache);
     592        });
     593    },
     594
     595    /**
     596     * @override
     597     * @param {Object} data
     598     * @param {Cache} cache
     599     */
     600    _doReplayCalls: function(data, cache)
     601    {
     602        var state = {};
     603        Object.keys(data.state).forEach(function(parameter) {
     604            state[parameter] = ReplayableResource.replay(data.state[parameter], cache);
     605        });
     606        this._state = state;
     607
     608        var gl = this._replayContextResource(data, cache).wrappedObject();
     609
     610        var bindingsData = {
     611            TEXTURE_2D: ["bindTexture", "TEXTURE_BINDING_2D"],
     612            TEXTURE_CUBE_MAP: ["bindTexture", "TEXTURE_BINDING_CUBE_MAP"],
     613            ARRAY_BUFFER: ["bindBuffer", "ARRAY_BUFFER_BINDING"],
     614            ELEMENT_ARRAY_BUFFER: ["bindBuffer", "ELEMENT_ARRAY_BUFFER_BINDING"],
     615            FRAMEBUFFER: ["bindFramebuffer", "FRAMEBUFFER_BINDING"],
     616            RENDERBUFFER: ["bindRenderbuffer", "RENDERBUFFER_BINDING"]
     617        };
     618        var originalBindings = {};
     619        Object.keys(bindingsData).forEach(function(bindingTarget) {
     620            var bindingParameter = bindingsData[bindingTarget][1];
     621            originalBindings[bindingTarget] = gl.getParameter(gl[bindingParameter]);
     622        });
     623
     624        Resource.prototype._doReplayCalls.call(this, data, cache);
     625
     626        Object.keys(bindingsData).forEach(function(bindingTarget) {
     627            var bindMethodName = bindingsData[bindingTarget][0];
     628            gl[bindMethodName].call(gl, gl[bindingTarget], originalBindings[bindingTarget]);
     629        });
     630    },
     631
     632    _replayContextResource: function(data, cache)
     633    {
     634        var calls = data.calls;
     635        for (var i = 0, n = calls.length; i < n; ++i) {
     636            var resource = ReplayableResource.replay(calls[i].resource(), cache);
     637            var contextResource = WebGLRenderingContextResource.forObject(resource);
     638            if (contextResource)
     639                return contextResource;
     640        }
     641        return null;
     642    },
     643
    451644    /**
    452645     * @param {number} target
     
    474667
    475668WebGLTextureResource.prototype = {
    476     /** @inheritDoc */
     669    /**
     670     * @override
     671     * @param {Object} data
     672     * @param {Cache} cache
     673     */
     674    _doReplayCalls: function(data, cache)
     675    {
     676        var gl = this._replayContextResource(data, cache).wrappedObject();
     677
     678        var state = {};
     679        WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
     680            state[parameter] = gl.getParameter(gl[parameter]);
     681        });
     682
     683        WebGLBoundResource.prototype._doReplayCalls.call(this, data, cache);
     684
     685        WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
     686            gl.pixelStorei(gl[parameter], state[parameter]);
     687        });
     688    },
     689
     690    /**
     691     * @override
     692     * @param {Call} call
     693     */
    477694    pushCall: function(call)
    478695    {
     
    537754}
    538755
     756/**
     757 * @type {Object.<number,string>}
     758 */
     759WebGLProgramResource.UniformMethodNames = null;
     760
    539761WebGLProgramResource.prototype = {
    540     /** @inheritDoc */
     762    /**
     763     * @override
     764     * @param {Object} data
     765     * @param {Cache} cache
     766     */
     767    _populateReplayableData: function(data, cache)
     768    {
     769        var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
     770        var program = this.wrappedObject();
     771
     772        var uniforms = [];
     773        var uniformsCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
     774        for (var i = 0; i < uniformsCount; ++i) {
     775            var activeInfo = gl.getActiveUniform(program, i);
     776            if (!activeInfo)
     777                continue;
     778            var uniformLocation = gl.getUniformLocation(program, activeInfo.name);
     779            if (!uniformLocation)
     780                continue;
     781            var value = gl.getUniform(program, uniformLocation);
     782            uniforms.push({
     783                name: activeInfo.name,
     784                type: activeInfo.type,
     785                value: value
     786            });
     787        }
     788        data.uniforms = uniforms;
     789    },
     790
     791    /**
     792     * @override
     793     * @param {Object} data
     794     * @param {Cache} cache
     795     */
     796    _doReplayCalls: function(data, cache)
     797    {
     798        Resource.prototype._doReplayCalls.call(this, data, cache);
     799        var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
     800        var program = this.wrappedObject();
     801
     802        var originalProgram = gl.getParameter(gl.CURRENT_PROGRAM);
     803        var currentProgram = originalProgram;
     804       
     805        data.uniforms.forEach(function(uniform) {
     806            var uniformLocation = gl.getUniformLocation(program, uniform.name);
     807            if (!uniformLocation)
     808                return;
     809            if (currentProgram !== program) {
     810                currentProgram = program;
     811                gl.useProgram(program);
     812            }
     813            var methodName = this._uniformMethodNameByType(gl, uniform.type);
     814            if (methodName.indexOf("Matrix") === -1)
     815                gl[methodName].call(gl, uniformLocation, uniform.value);
     816            else
     817                gl[methodName].call(gl, uniformLocation, false, uniform.value);
     818        }.bind(this));
     819
     820        if (currentProgram !== originalProgram)
     821            gl.useProgram(originalProgram);
     822    },
     823
     824    /**
     825     * @param {WebGLRenderingContext} gl
     826     * @param {number} type
     827     * @return {string}
     828     */
     829    _uniformMethodNameByType: function(gl, type)
     830    {
     831        var uniformMethodNames = WebGLProgramResource.UniformMethodNames;
     832        if (!uniformMethodNames) {
     833            uniformMethodNames = {};
     834            uniformMethodNames[gl.FLOAT] = "uniform1f";
     835            uniformMethodNames[gl.FLOAT_VEC2] = "uniform2fv";
     836            uniformMethodNames[gl.FLOAT_VEC3] = "uniform3fv";
     837            uniformMethodNames[gl.FLOAT_VEC4] = "uniform4fv";
     838            uniformMethodNames[gl.INT] = "uniform1i";
     839            uniformMethodNames[gl.BOOL] = "uniform1i";
     840            uniformMethodNames[gl.SAMPLER_2D] = "uniform1i";
     841            uniformMethodNames[gl.SAMPLER_CUBE] = "uniform1i";
     842            uniformMethodNames[gl.INT_VEC2] = "uniform2iv";
     843            uniformMethodNames[gl.BOOL_VEC2] = "uniform2iv";
     844            uniformMethodNames[gl.INT_VEC3] = "uniform3iv";
     845            uniformMethodNames[gl.BOOL_VEC3] = "uniform3iv";
     846            uniformMethodNames[gl.INT_VEC4] = "uniform4iv";
     847            uniformMethodNames[gl.BOOL_VEC4] = "uniform4iv";
     848            uniformMethodNames[gl.FLOAT_MAT2] = "uniformMatrix2fv";
     849            uniformMethodNames[gl.FLOAT_MAT3] = "uniformMatrix3fv";
     850            uniformMethodNames[gl.FLOAT_MAT4] = "uniformMatrix4fv";
     851            WebGLProgramResource.UniformMethodNames = uniformMethodNames;
     852        }
     853        console.assert(uniformMethodNames[type], "Unknown uniform type " + type);
     854        return uniformMethodNames[type];
     855    },
     856
     857    /**
     858     * @override
     859     * @param {Call} call
     860     */
    541861    pushCall: function(call)
    542862    {
     
    559879
    560880WebGLShaderResource.prototype = {
    561     /** @inheritDoc */
     881    /**
     882     * @override
     883     * @param {Call} call
     884     */
    562885    pushCall: function(call)
    563886    {
     
    580903
    581904WebGLBufferResource.prototype = {
    582     /** @inheritDoc */
     905    /**
     906     * @override
     907     * @param {Call} call
     908     */
    583909    pushCall: function(call)
    584910    {
     
    601927
    602928WebGLFramebufferResource.prototype = {
    603     /** @inheritDoc */
     929    /**
     930     * @override
     931     * @param {Call} call
     932     */
    604933    pushCall: function(call)
    605934    {
     
    621950
    622951WebGLRenderbufferResource.prototype = {
    623     /** @inheritDoc */
     952    /**
     953     * @override
     954     * @param {Call} call
     955     */
    624956    pushCall: function(call)
    625957    {
     
    635967 * @extends {Resource}
    636968 * @param {WebGLRenderingContext} glContext
    637  */
    638 function WebGLRenderingContextResource(glContext)
     969 * @param {Function} replayContextCallback
     970 */
     971function WebGLRenderingContextResource(glContext, replayContextCallback)
    639972{
    640973    Resource.call(this, glContext);
    641974    this._proxyObject = null;
    642 }
     975    this._replayContextCallback = replayContextCallback;
     976}
     977
     978/**
     979 * @const
     980 * @type {Array.<string>}
     981 */
     982WebGLRenderingContextResource.GLCapabilities = [
     983    "BLEND",
     984    "CULL_FACE",
     985    "DEPTH_TEST",
     986    "DITHER",
     987    "POLYGON_OFFSET_FILL",
     988    "SAMPLE_ALPHA_TO_COVERAGE",
     989    "SAMPLE_COVERAGE",
     990    "SCISSOR_TEST",
     991    "STENCIL_TEST"
     992];
    643993
    644994/**
     
    6521002    "UNPACK_FLIP_Y_WEBGL",
    6531003    "UNPACK_PREMULTIPLY_ALPHA_WEBGL"
     1004];
     1005
     1006/**
     1007 * @const
     1008 * @type {Array.<string>}
     1009 */
     1010WebGLRenderingContextResource.StateParameters = [
     1011    "ACTIVE_TEXTURE",
     1012    "ARRAY_BUFFER_BINDING",
     1013    "BLEND_COLOR",
     1014    "BLEND_DST_ALPHA",
     1015    "BLEND_DST_RGB",
     1016    "BLEND_EQUATION_ALPHA",
     1017    "BLEND_EQUATION_RGB",
     1018    "BLEND_SRC_ALPHA",
     1019    "BLEND_SRC_RGB",
     1020    "COLOR_CLEAR_VALUE",
     1021    "COLOR_WRITEMASK",
     1022    "CULL_FACE_MODE",
     1023    "CURRENT_PROGRAM",
     1024    "DEPTH_CLEAR_VALUE",
     1025    "DEPTH_FUNC",
     1026    "DEPTH_RANGE",
     1027    "DEPTH_WRITEMASK",
     1028    "ELEMENT_ARRAY_BUFFER_BINDING",
     1029    "FRAMEBUFFER_BINDING",
     1030    "FRONT_FACE",
     1031    "GENERATE_MIPMAP_HINT",
     1032    "LINE_WIDTH",
     1033    "PACK_ALIGNMENT",
     1034    "POLYGON_OFFSET_FACTOR",
     1035    "POLYGON_OFFSET_UNITS",
     1036    "RENDERBUFFER_BINDING",
     1037    "SAMPLE_COVERAGE_INVERT",
     1038    "SAMPLE_COVERAGE_VALUE",
     1039    "SCISSOR_BOX",
     1040    "STENCIL_BACK_FAIL",
     1041    "STENCIL_BACK_FUNC",
     1042    "STENCIL_BACK_PASS_DEPTH_FAIL",
     1043    "STENCIL_BACK_PASS_DEPTH_PASS",
     1044    "STENCIL_BACK_REF",
     1045    "STENCIL_BACK_VALUE_MASK",
     1046    "STENCIL_BACK_WRITEMASK",
     1047    "STENCIL_CLEAR_VALUE",
     1048    "STENCIL_FAIL",
     1049    "STENCIL_FUNC",
     1050    "STENCIL_PASS_DEPTH_FAIL",
     1051    "STENCIL_PASS_DEPTH_PASS",
     1052    "STENCIL_REF",
     1053    "STENCIL_VALUE_MASK",
     1054    "STENCIL_WRITEMASK",
     1055    "UNPACK_ALIGNMENT",
     1056    "UNPACK_COLORSPACE_CONVERSION_WEBGL",
     1057    "UNPACK_FLIP_Y_WEBGL",
     1058    "UNPACK_PREMULTIPLY_ALPHA_WEBGL",
     1059    "VIEWPORT"
    6541060];
    6551061
     
    6821088
    6831089    /**
     1090     * @override
     1091     * @param {Object} data
     1092     * @param {Cache} cache
     1093     */
     1094    _populateReplayableData: function(data, cache)
     1095    {
     1096        var gl = this.wrappedObject();
     1097        data.replayContextCallback = this._replayContextCallback;
     1098
     1099        // FIXME: Save the getError() status and restore it after taking the GL state snapshot.
     1100
     1101        // Take a full GL state snapshot.
     1102        var glState = {};
     1103        WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {
     1104            glState[parameter] = gl.isEnabled(gl[parameter]);
     1105        });
     1106        WebGLRenderingContextResource.StateParameters.forEach(function(parameter) {
     1107            glState[parameter] = Resource.toReplayable(gl.getParameter(gl[parameter]), cache);
     1108            // FIXME: Call while(gl.getError() != gl.NO_ERROR) {...} to check if a particular parameter is supported.
     1109        });
     1110
     1111        // VERTEX_ATTRIB_ARRAYS
     1112        var maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
     1113        var vertexAttribParameters = ["VERTEX_ATTRIB_ARRAY_BUFFER_BINDING", "VERTEX_ATTRIB_ARRAY_ENABLED", "VERTEX_ATTRIB_ARRAY_SIZE", "VERTEX_ATTRIB_ARRAY_STRIDE", "VERTEX_ATTRIB_ARRAY_TYPE", "VERTEX_ATTRIB_ARRAY_NORMALIZED", "CURRENT_VERTEX_ATTRIB"];
     1114        var vertexAttribStates = [];
     1115        for (var i = 0; i < maxVertexAttribs; ++i) {
     1116            var state = {};
     1117            vertexAttribParameters.forEach(function(attribParameter) {
     1118                state[attribParameter] = Resource.toReplayable(gl.getVertexAttrib(i, gl[attribParameter]), cache);
     1119            });
     1120            state.VERTEX_ATTRIB_ARRAY_POINTER = gl.getVertexAttribOffset(i, gl.VERTEX_ATTRIB_ARRAY_POINTER);
     1121            vertexAttribStates.push(state);
     1122        }
     1123        glState.vertexAttribStates = vertexAttribStates;
     1124
     1125        // TEXTURES
     1126        var currentTextureBinding = gl.getParameter(gl.ACTIVE_TEXTURE);
     1127        var maxTextureImageUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
     1128        var textureBindings = [];
     1129        for (var i = 0; i < maxTextureImageUnits; ++i) {
     1130            gl.activeTexture(gl.TEXTURE0 + i);
     1131            var state = {
     1132                TEXTURE_2D: Resource.toReplayable(gl.getParameter(gl.TEXTURE_BINDING_2D), cache),
     1133                TEXTURE_CUBE_MAP: Resource.toReplayable(gl.getParameter(gl.TEXTURE_BINDING_CUBE_MAP), cache)
     1134            };
     1135            textureBindings.push(state);
     1136        }
     1137        glState.textureBindings = textureBindings;
     1138        gl.activeTexture(currentTextureBinding);
     1139
     1140        data.glState = glState;
     1141    },
     1142
     1143    /**
     1144     * @override
     1145     * @param {Object} data
     1146     * @param {Cache} cache
     1147     */
     1148    _doReplayCalls: function(data, cache)
     1149    {
     1150        this._proxyObject = null;
     1151        this._replayContextCallback = data.replayContextCallback;
     1152
     1153        var context = this._replayContextCallback();
     1154        var contextResource = Resource.forObject(context);
     1155        var gl = contextResource ? contextResource.wrappedObject() : context;
     1156        this.setWrappedObject(gl);
     1157
     1158        var glState = data.glState;
     1159        gl.bindFramebuffer(gl.FRAMEBUFFER, ReplayableResource.replay(glState.FRAMEBUFFER_BINDING, cache));
     1160        gl.bindRenderbuffer(gl.RENDERBUFFER, ReplayableResource.replay(glState.RENDERBUFFER_BINDING, cache));
     1161
     1162        // Enable or disable server-side GL capabilities.
     1163        WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {
     1164            console.assert(parameter in glState);
     1165            if (glState[parameter])
     1166                gl.enable(gl[parameter]);
     1167            else
     1168                gl.disable(gl[parameter]);
     1169        });
     1170
     1171        gl.blendColor(glState.BLEND_COLOR[0], glState.BLEND_COLOR[1], glState.BLEND_COLOR[2], glState.BLEND_COLOR[3]);
     1172        gl.blendEquationSeparate(glState.BLEND_EQUATION_RGB, glState.BLEND_EQUATION_ALPHA);
     1173        gl.blendFuncSeparate(glState.BLEND_SRC_RGB, glState.BLEND_DST_RGB, glState.BLEND_SRC_ALPHA, glState.BLEND_DST_ALPHA);
     1174        gl.clearColor(glState.COLOR_CLEAR_VALUE[0], glState.COLOR_CLEAR_VALUE[1], glState.COLOR_CLEAR_VALUE[2], glState.COLOR_CLEAR_VALUE[3]);
     1175        gl.clearDepth(glState.DEPTH_CLEAR_VALUE);
     1176        gl.clearStencil(glState.STENCIL_CLEAR_VALUE);
     1177        gl.colorMask(glState.COLOR_WRITEMASK[0], glState.COLOR_WRITEMASK[1], glState.COLOR_WRITEMASK[2], glState.COLOR_WRITEMASK[3]);
     1178        gl.cullFace(glState.CULL_FACE_MODE);
     1179        gl.depthFunc(glState.DEPTH_FUNC);
     1180        gl.depthMask(glState.DEPTH_WRITEMASK);
     1181        gl.depthRange(glState.DEPTH_RANGE[0], glState.DEPTH_RANGE[1]);
     1182        gl.frontFace(glState.FRONT_FACE);
     1183        gl.hint(gl.GENERATE_MIPMAP_HINT, glState.GENERATE_MIPMAP_HINT);
     1184        gl.lineWidth(glState.LINE_WIDTH);
     1185
     1186        WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
     1187            gl.pixelStorei(gl[parameter], glState[parameter]);
     1188        });
     1189
     1190        gl.polygonOffset(glState.POLYGON_OFFSET_FACTOR, glState.POLYGON_OFFSET_UNITS);
     1191        gl.sampleCoverage(glState.SAMPLE_COVERAGE_VALUE, glState.SAMPLE_COVERAGE_INVERT);
     1192        gl.stencilFuncSeparate(gl.FRONT, glState.STENCIL_FUNC, glState.STENCIL_REF, glState.STENCIL_VALUE_MASK);
     1193        gl.stencilFuncSeparate(gl.BACK, glState.STENCIL_BACK_FUNC, glState.STENCIL_BACK_REF, glState.STENCIL_BACK_VALUE_MASK);
     1194        gl.stencilOpSeparate(gl.FRONT, glState.STENCIL_FAIL, glState.STENCIL_PASS_DEPTH_FAIL, glState.STENCIL_PASS_DEPTH_PASS);
     1195        gl.stencilOpSeparate(gl.BACK, glState.STENCIL_BACK_FAIL, glState.STENCIL_BACK_PASS_DEPTH_FAIL, glState.STENCIL_BACK_PASS_DEPTH_PASS);
     1196        gl.stencilMaskSeparate(gl.FRONT, glState.STENCIL_WRITEMASK);
     1197        gl.stencilMaskSeparate(gl.BACK, glState.STENCIL_BACK_WRITEMASK);
     1198
     1199        gl.scissor(glState.SCISSOR_BOX[0], glState.SCISSOR_BOX[1], glState.SCISSOR_BOX[2], glState.SCISSOR_BOX[3]);
     1200        gl.viewport(glState.VIEWPORT[0], glState.VIEWPORT[1], glState.VIEWPORT[2], glState.VIEWPORT[3]);
     1201
     1202        gl.useProgram(ReplayableResource.replay(glState.CURRENT_PROGRAM, cache));
     1203
     1204        // VERTEX_ATTRIB_ARRAYS
     1205        var maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
     1206        for (var i = 0; i < maxVertexAttribs; ++i) {
     1207            var state = glState.vertexAttribStates[i] || {};
     1208            if (state.VERTEX_ATTRIB_ARRAY_ENABLED)
     1209                gl.enableVertexAttribArray(i);
     1210            else
     1211                gl.disableVertexAttribArray(i);
     1212            if (state.CURRENT_VERTEX_ATTRIB)
     1213                gl.vertexAttrib4fv(i, state.CURRENT_VERTEX_ATTRIB);
     1214            var buffer = ReplayableResource.replay(state.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, cache);
     1215            if (buffer) {
     1216                gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
     1217                gl.vertexAttribPointer(i, state.VERTEX_ATTRIB_ARRAY_SIZE, state.VERTEX_ATTRIB_ARRAY_TYPE, state.VERTEX_ATTRIB_ARRAY_NORMALIZED, state.VERTEX_ATTRIB_ARRAY_STRIDE, state.VERTEX_ATTRIB_ARRAY_POINTER);
     1218            }
     1219        }
     1220        gl.bindBuffer(gl.ARRAY_BUFFER, ReplayableResource.replay(glState.ARRAY_BUFFER_BINDING, cache));
     1221        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ReplayableResource.replay(glState.ELEMENT_ARRAY_BUFFER_BINDING, cache));
     1222
     1223        // TEXTURES
     1224        var maxTextureImageUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
     1225        for (var i = 0; i < maxTextureImageUnits; ++i) {
     1226            gl.activeTexture(gl.TEXTURE0 + i);
     1227            var state = glState.textureBindings[i] || {};
     1228            gl.bindTexture(gl.TEXTURE_2D, ReplayableResource.replay(state.TEXTURE_2D, cache));
     1229            gl.bindTexture(gl.TEXTURE_CUBE_MAP, ReplayableResource.replay(state.TEXTURE_CUBE_MAP, cache));
     1230        }
     1231        gl.activeTexture(glState.ACTIVE_TEXTURE);
     1232
     1233        return Resource.prototype._doReplayCalls.call(this, data, cache);
     1234    },
     1235
     1236    /**
    6841237     * @param {Object|number} target
    6851238     * @return {Resource}
     
    6941247        var bindMethodName;
    6951248        switch (target) {
    696             case gl.ARRAY_BUFFER:
    697                 bindingTarget = gl.ARRAY_BUFFER_BINDING;
    698                 bindMethodName = "bindBuffer";
    699                 break;
    700             case gl.ELEMENT_ARRAY_BUFFER:
    701                 bindingTarget = gl.ELEMENT_ARRAY_BUFFER_BINDING;
    702                 bindMethodName = "bindBuffer";
    703                 break;
    704             case gl.TEXTURE_2D:
    705                 bindingTarget = gl.TEXTURE_BINDING_2D;
    706                 bindMethodName = "bindTexture";
    707                 break;
    708             case gl.TEXTURE_CUBE_MAP:
    709             case gl.TEXTURE_CUBE_MAP_POSITIVE_X:
    710             case gl.TEXTURE_CUBE_MAP_NEGATIVE_X:
    711             case gl.TEXTURE_CUBE_MAP_POSITIVE_Y:
    712             case gl.TEXTURE_CUBE_MAP_NEGATIVE_Y:
    713             case gl.TEXTURE_CUBE_MAP_POSITIVE_Z:
    714             case gl.TEXTURE_CUBE_MAP_NEGATIVE_Z:
    715                 bindingTarget = gl.TEXTURE_BINDING_CUBE_MAP;
    716                 bindMethodName = "bindTexture";
    717                 break;
    718             case gl.FRAMEBUFFER:
    719                 bindingTarget = gl.FRAMEBUFFER_BINDING;
    720                 bindMethodName = "bindFramebuffer";
    721                 break;
    722             case gl.RENDERBUFFER:
    723                 bindingTarget = gl.RENDERBUFFER_BINDING;
    724                 bindMethodName = "bindRenderbuffer";
    725                 break;
    726             default:
    727                 console.error("ASSERT_NOT_REACHED: unknown binding target " + target);
    728                 return null;
     1249        case gl.ARRAY_BUFFER:
     1250            bindingTarget = gl.ARRAY_BUFFER_BINDING;
     1251            bindMethodName = "bindBuffer";
     1252            break;
     1253        case gl.ELEMENT_ARRAY_BUFFER:
     1254            bindingTarget = gl.ELEMENT_ARRAY_BUFFER_BINDING;
     1255            bindMethodName = "bindBuffer";
     1256            break;
     1257        case gl.TEXTURE_2D:
     1258            bindingTarget = gl.TEXTURE_BINDING_2D;
     1259            bindMethodName = "bindTexture";
     1260            break;
     1261        case gl.TEXTURE_CUBE_MAP:
     1262        case gl.TEXTURE_CUBE_MAP_POSITIVE_X:
     1263        case gl.TEXTURE_CUBE_MAP_NEGATIVE_X:
     1264        case gl.TEXTURE_CUBE_MAP_POSITIVE_Y:
     1265        case gl.TEXTURE_CUBE_MAP_NEGATIVE_Y:
     1266        case gl.TEXTURE_CUBE_MAP_POSITIVE_Z:
     1267        case gl.TEXTURE_CUBE_MAP_NEGATIVE_Z:
     1268            bindingTarget = gl.TEXTURE_BINDING_CUBE_MAP;
     1269            bindMethodName = "bindTexture";
     1270            break;
     1271        case gl.FRAMEBUFFER:
     1272            bindingTarget = gl.FRAMEBUFFER_BINDING;
     1273            bindMethodName = "bindFramebuffer";
     1274            break;
     1275        case gl.RENDERBUFFER:
     1276            bindingTarget = gl.RENDERBUFFER_BINDING;
     1277            bindMethodName = "bindRenderbuffer";
     1278            break;
     1279        default:
     1280            console.error("ASSERT_NOT_REACHED: unknown binding target " + target);
     1281            return null;
    7291282        }
    7301283        resource = Resource.forObject(gl.getParameter(bindingTarget));
     
    9661519    captureResource: function(resource)
    9671520    {
    968         // FIXME: Capture current resource state to start the replay from.
     1521        resource.toReplayable(this._replayablesCache);
    9691522    },
    9701523
     
    9741527    addCall: function(call)
    9751528    {
    976         // FIXME: Convert the call to a ReplayableCall and push it.
     1529        var res = Resource.forObject(call.result());
     1530        if (res)
     1531            this.captureResource(res);
     1532        var size = this._replayablesCache.size();
     1533        this._replayableCalls.push(call.toReplayable(this._replayablesCache));
     1534        console.assert(this._replayablesCache.size() === size, "Internal error: We should have captured all the resources already by this time.");
    9771535    }
    9781536}
     
    11631721    wrapWebGLContext: function(glContext)
    11641722    {
    1165         var resource = Resource.forObject(glContext) || new WebGLRenderingContextResource(glContext);
     1723        var resource = Resource.forObject(glContext) || new WebGLRenderingContextResource(glContext, this._constructReplayContext.bind(this, glContext));
    11661724        this._manager.registerResource(resource);
    11671725        var proxy = resource.proxyObject();
     
    12281786        }
    12291787        // Return current screenshot.
     1788        // FIXME: Support replaying several canvases simultaneously.
    12301789        return this._replayContext.canvas.toDataURL();
    12311790    },
     
    12371796    {
    12381797        return "{\"injectedScriptId\":" + injectedScriptId + ",\"traceLogId\":" + (++this._lastTraceLogId) + "}";
     1798    },
     1799
     1800    /**
     1801     * @param {WebGLRenderingContext} originalGlContext
     1802     * @return {WebGLRenderingContext}
     1803     */
     1804    _constructReplayContext: function(originalGlContext)
     1805    {
     1806        var replayContext = originalGlContext["__replayContext"];
     1807        if (!replayContext) {
     1808            var canvas = originalGlContext.canvas.cloneNode(true);
     1809            // FIXME: Pass original context id instead of "experimental-webgl".
     1810            // FIXME: Pass original ContextAttributes to the getContext() method.
     1811            replayContext = canvas.getContext("experimental-webgl");
     1812            originalGlContext["__replayContext"] = replayContext;
     1813            this._replayContext = replayContext;
     1814        } else {
     1815            // FIXME: Reset the replay GL state and clear the canvas.
     1816        }
     1817        return replayContext;
    12391818    }
    12401819}
Note: See TracChangeset for help on using the changeset viewer.