1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/devtools/server/actors/webgl.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1319 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +"use strict"; 1.8 + 1.9 +const {Cc, Ci, Cu, Cr} = require("chrome"); 1.10 +const events = require("sdk/event/core"); 1.11 +const protocol = require("devtools/server/protocol"); 1.12 +const { ContentObserver } = require("devtools/content-observer"); 1.13 + 1.14 +const { on, once, off, emit } = events; 1.15 +const { method, Arg, Option, RetVal } = protocol; 1.16 + 1.17 +const WEBGL_CONTEXT_NAMES = ["webgl", "experimental-webgl", "moz-webgl"]; 1.18 + 1.19 +// These traits are bit masks. Make sure they're powers of 2. 1.20 +const PROGRAM_DEFAULT_TRAITS = 0; 1.21 +const PROGRAM_BLACKBOX_TRAIT = 1; 1.22 +const PROGRAM_HIGHLIGHT_TRAIT = 2; 1.23 + 1.24 +exports.register = function(handle) { 1.25 + handle.addTabActor(WebGLActor, "webglActor"); 1.26 +} 1.27 + 1.28 +exports.unregister = function(handle) { 1.29 + handle.removeTabActor(WebGLActor); 1.30 +} 1.31 + 1.32 +/** 1.33 + * A WebGL Shader contributing to building a WebGL Program. 1.34 + * You can either retrieve, or compile the source of a shader, which will 1.35 + * automatically inflict the necessary changes to the WebGL state. 1.36 + */ 1.37 +let ShaderActor = protocol.ActorClass({ 1.38 + typeName: "gl-shader", 1.39 + 1.40 + /** 1.41 + * Create the shader actor. 1.42 + * 1.43 + * @param DebuggerServerConnection conn 1.44 + * The server connection. 1.45 + * @param WebGLProgram program 1.46 + * The WebGL program being linked. 1.47 + * @param WebGLShader shader 1.48 + * The cooresponding vertex or fragment shader. 1.49 + * @param WebGLProxy proxy 1.50 + * The proxy methods for the WebGL context owning this shader. 1.51 + */ 1.52 + initialize: function(conn, program, shader, proxy) { 1.53 + protocol.Actor.prototype.initialize.call(this, conn); 1.54 + this.program = program; 1.55 + this.shader = shader; 1.56 + this.text = proxy.getShaderSource(shader); 1.57 + this.linkedProxy = proxy; 1.58 + }, 1.59 + 1.60 + /** 1.61 + * Gets the source code for this shader. 1.62 + */ 1.63 + getText: method(function() { 1.64 + return this.text; 1.65 + }, { 1.66 + response: { text: RetVal("string") } 1.67 + }), 1.68 + 1.69 + /** 1.70 + * Sets and compiles new source code for this shader. 1.71 + */ 1.72 + compile: method(function(text) { 1.73 + // Get the shader and corresponding program to change via the WebGL proxy. 1.74 + let { linkedProxy: proxy, shader, program } = this; 1.75 + 1.76 + // Get the new shader source to inject. 1.77 + let oldText = this.text; 1.78 + let newText = text; 1.79 + 1.80 + // Overwrite the shader's source. 1.81 + let error = proxy.compileShader(program, shader, this.text = newText); 1.82 + 1.83 + // If something went wrong, revert to the previous shader. 1.84 + if (error.compile || error.link) { 1.85 + proxy.compileShader(program, shader, this.text = oldText); 1.86 + return error; 1.87 + } 1.88 + return undefined; 1.89 + }, { 1.90 + request: { text: Arg(0, "string") }, 1.91 + response: { error: RetVal("nullable:json") } 1.92 + }) 1.93 +}); 1.94 + 1.95 +/** 1.96 + * The corresponding Front object for the ShaderActor. 1.97 + */ 1.98 +let ShaderFront = protocol.FrontClass(ShaderActor, { 1.99 + initialize: function(client, form) { 1.100 + protocol.Front.prototype.initialize.call(this, client, form); 1.101 + } 1.102 +}); 1.103 + 1.104 +/** 1.105 + * A WebGL program is composed (at the moment, analogue to OpenGL ES 2.0) 1.106 + * of two shaders: a vertex shader and a fragment shader. 1.107 + */ 1.108 +let ProgramActor = protocol.ActorClass({ 1.109 + typeName: "gl-program", 1.110 + 1.111 + /** 1.112 + * Create the program actor. 1.113 + * 1.114 + * @param DebuggerServerConnection conn 1.115 + * The server connection. 1.116 + * @param WebGLProgram program 1.117 + * The WebGL program being linked. 1.118 + * @param WebGLShader[] shaders 1.119 + * The WebGL program's cooresponding vertex and fragment shaders. 1.120 + * @param WebGLCache cache 1.121 + * The state storage for the WebGL context owning this program. 1.122 + * @param WebGLProxy proxy 1.123 + * The proxy methods for the WebGL context owning this program. 1.124 + */ 1.125 + initialize: function(conn, [program, shaders, cache, proxy]) { 1.126 + protocol.Actor.prototype.initialize.call(this, conn); 1.127 + this._shaderActorsCache = { vertex: null, fragment: null }; 1.128 + this.program = program; 1.129 + this.shaders = shaders; 1.130 + this.linkedCache = cache; 1.131 + this.linkedProxy = proxy; 1.132 + }, 1.133 + 1.134 + get ownerWindow() this.linkedCache.ownerWindow, 1.135 + get ownerContext() this.linkedCache.ownerContext, 1.136 + 1.137 + /** 1.138 + * Gets the vertex shader linked to this program. This method guarantees 1.139 + * a single actor instance per shader. 1.140 + */ 1.141 + getVertexShader: method(function() { 1.142 + return this._getShaderActor("vertex"); 1.143 + }, { 1.144 + response: { shader: RetVal("gl-shader") } 1.145 + }), 1.146 + 1.147 + /** 1.148 + * Gets the fragment shader linked to this program. This method guarantees 1.149 + * a single actor instance per shader. 1.150 + */ 1.151 + getFragmentShader: method(function() { 1.152 + return this._getShaderActor("fragment"); 1.153 + }, { 1.154 + response: { shader: RetVal("gl-shader") } 1.155 + }), 1.156 + 1.157 + /** 1.158 + * Highlights any geometry rendered using this program. 1.159 + */ 1.160 + highlight: method(function(tint) { 1.161 + this.linkedProxy.highlightTint = tint; 1.162 + this.linkedCache.setProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT); 1.163 + }, { 1.164 + request: { tint: Arg(0, "array:number") }, 1.165 + oneway: true 1.166 + }), 1.167 + 1.168 + /** 1.169 + * Allows geometry to be rendered normally using this program. 1.170 + */ 1.171 + unhighlight: method(function() { 1.172 + this.linkedCache.unsetProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT); 1.173 + }, { 1.174 + oneway: true 1.175 + }), 1.176 + 1.177 + /** 1.178 + * Prevents any geometry from being rendered using this program. 1.179 + */ 1.180 + blackbox: method(function() { 1.181 + this.linkedCache.setProgramTrait(this.program, PROGRAM_BLACKBOX_TRAIT); 1.182 + }, { 1.183 + oneway: true 1.184 + }), 1.185 + 1.186 + /** 1.187 + * Allows geometry to be rendered using this program. 1.188 + */ 1.189 + unblackbox: method(function() { 1.190 + this.linkedCache.unsetProgramTrait(this.program, PROGRAM_BLACKBOX_TRAIT); 1.191 + }, { 1.192 + oneway: true 1.193 + }), 1.194 + 1.195 + /** 1.196 + * Returns a cached ShaderActor instance based on the required shader type. 1.197 + * 1.198 + * @param string type 1.199 + * Either "vertex" or "fragment". 1.200 + * @return ShaderActor 1.201 + * The respective shader actor instance. 1.202 + */ 1.203 + _getShaderActor: function(type) { 1.204 + if (this._shaderActorsCache[type]) { 1.205 + return this._shaderActorsCache[type]; 1.206 + } 1.207 + let proxy = this.linkedProxy; 1.208 + let shader = proxy.getShaderOfType(this.shaders, type); 1.209 + let shaderActor = new ShaderActor(this.conn, this.program, shader, proxy); 1.210 + return this._shaderActorsCache[type] = shaderActor; 1.211 + } 1.212 +}); 1.213 + 1.214 +/** 1.215 + * The corresponding Front object for the ProgramActor. 1.216 + */ 1.217 +let ProgramFront = protocol.FrontClass(ProgramActor, { 1.218 + initialize: function(client, form) { 1.219 + protocol.Front.prototype.initialize.call(this, client, form); 1.220 + } 1.221 +}); 1.222 + 1.223 +/** 1.224 + * The WebGL Actor handles simple interaction with a WebGL context via a few 1.225 + * high-level methods. After instantiating this actor, you'll need to set it 1.226 + * up by calling setup(). 1.227 + */ 1.228 +let WebGLActor = exports.WebGLActor = protocol.ActorClass({ 1.229 + typeName: "webgl", 1.230 + initialize: function(conn, tabActor) { 1.231 + protocol.Actor.prototype.initialize.call(this, conn); 1.232 + this.tabActor = tabActor; 1.233 + this._onGlobalCreated = this._onGlobalCreated.bind(this); 1.234 + this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this); 1.235 + this._onProgramLinked = this._onProgramLinked.bind(this); 1.236 + }, 1.237 + destroy: function(conn) { 1.238 + protocol.Actor.prototype.destroy.call(this, conn); 1.239 + this.finalize(); 1.240 + }, 1.241 + 1.242 + /** 1.243 + * Starts waiting for the current tab actor's document global to be 1.244 + * created, in order to instrument the Canvas context and become 1.245 + * aware of everything the content does WebGL-wise. 1.246 + * 1.247 + * See ContentObserver and WebGLInstrumenter for more details. 1.248 + */ 1.249 + setup: method(function({ reload }) { 1.250 + if (this._initialized) { 1.251 + return; 1.252 + } 1.253 + this._initialized = true; 1.254 + 1.255 + this._programActorsCache = []; 1.256 + this._contentObserver = new ContentObserver(this.tabActor); 1.257 + this._webglObserver = new WebGLObserver(); 1.258 + 1.259 + on(this._contentObserver, "global-created", this._onGlobalCreated); 1.260 + on(this._contentObserver, "global-destroyed", this._onGlobalDestroyed); 1.261 + on(this._webglObserver, "program-linked", this._onProgramLinked); 1.262 + 1.263 + if (reload) { 1.264 + this.tabActor.window.location.reload(); 1.265 + } 1.266 + }, { 1.267 + request: { reload: Option(0, "boolean") }, 1.268 + oneway: true 1.269 + }), 1.270 + 1.271 + /** 1.272 + * Stops listening for document global changes and puts this actor 1.273 + * to hibernation. This method is called automatically just before the 1.274 + * actor is destroyed. 1.275 + */ 1.276 + finalize: method(function() { 1.277 + if (!this._initialized) { 1.278 + return; 1.279 + } 1.280 + this._initialized = false; 1.281 + 1.282 + this._contentObserver.stopListening(); 1.283 + off(this._contentObserver, "global-created", this._onGlobalCreated); 1.284 + off(this._contentObserver, "global-destroyed", this._onGlobalDestroyed); 1.285 + off(this._webglObserver, "program-linked", this._onProgramLinked); 1.286 + 1.287 + this._programActorsCache = null; 1.288 + this._contentObserver = null; 1.289 + this._webglObserver = null; 1.290 + }, { 1.291 + oneway: true 1.292 + }), 1.293 + 1.294 + /** 1.295 + * Gets an array of cached program actors for the current tab actor's window. 1.296 + * This is useful for dealing with bfcache, when no new programs are linked. 1.297 + */ 1.298 + getPrograms: method(function() { 1.299 + let id = ContentObserver.GetInnerWindowID(this.tabActor.window); 1.300 + return this._programActorsCache.filter(e => e.ownerWindow == id); 1.301 + }, { 1.302 + response: { programs: RetVal("array:gl-program") } 1.303 + }), 1.304 + 1.305 + /** 1.306 + * Events emitted by this actor. The "program-linked" event is fired 1.307 + * every time a WebGL program was linked with its respective two shaders. 1.308 + */ 1.309 + events: { 1.310 + "program-linked": { 1.311 + type: "programLinked", 1.312 + program: Arg(0, "gl-program") 1.313 + } 1.314 + }, 1.315 + 1.316 + /** 1.317 + * Invoked whenever the current tab actor's document global is created. 1.318 + */ 1.319 + _onGlobalCreated: function(window) { 1.320 + WebGLInstrumenter.handle(window, this._webglObserver); 1.321 + }, 1.322 + 1.323 + /** 1.324 + * Invoked whenever the current tab actor's inner window is destroyed. 1.325 + */ 1.326 + _onGlobalDestroyed: function(id) { 1.327 + removeFromArray(this._programActorsCache, e => e.ownerWindow == id); 1.328 + this._webglObserver.unregisterContextsForWindow(id); 1.329 + }, 1.330 + 1.331 + /** 1.332 + * Invoked whenever an observed WebGL context links a program. 1.333 + */ 1.334 + _onProgramLinked: function(...args) { 1.335 + let programActor = new ProgramActor(this.conn, args); 1.336 + this._programActorsCache.push(programActor); 1.337 + events.emit(this, "program-linked", programActor); 1.338 + } 1.339 +}); 1.340 + 1.341 +/** 1.342 + * The corresponding Front object for the WebGLActor. 1.343 + */ 1.344 +let WebGLFront = exports.WebGLFront = protocol.FrontClass(WebGLActor, { 1.345 + initialize: function(client, { webglActor }) { 1.346 + protocol.Front.prototype.initialize.call(this, client, { actor: webglActor }); 1.347 + client.addActorPool(this); 1.348 + this.manage(this); 1.349 + } 1.350 +}); 1.351 + 1.352 +/** 1.353 + * Instruments a HTMLCanvasElement with the appropriate inspection methods. 1.354 + */ 1.355 +let WebGLInstrumenter = { 1.356 + /** 1.357 + * Overrides the getContext method in the HTMLCanvasElement prototype. 1.358 + * 1.359 + * @param nsIDOMWindow window 1.360 + * The window to perform the instrumentation in. 1.361 + * @param WebGLObserver observer 1.362 + * The observer watching function calls in the context. 1.363 + */ 1.364 + handle: function(window, observer) { 1.365 + let self = this; 1.366 + 1.367 + let id = ContentObserver.GetInnerWindowID(window); 1.368 + let canvasElem = XPCNativeWrapper.unwrap(window.HTMLCanvasElement); 1.369 + let canvasPrototype = canvasElem.prototype; 1.370 + let originalGetContext = canvasPrototype.getContext; 1.371 + 1.372 + /** 1.373 + * Returns a drawing context on the canvas, or null if the context ID is 1.374 + * not supported. This override creates an observer for the targeted context 1.375 + * type and instruments specific functions in the targeted context instance. 1.376 + */ 1.377 + canvasPrototype.getContext = function(name, options) { 1.378 + // Make sure a context was able to be created. 1.379 + let context = originalGetContext.call(this, name, options); 1.380 + if (!context) { 1.381 + return context; 1.382 + } 1.383 + // Make sure a WebGL (not a 2D) context will be instrumented. 1.384 + if (WEBGL_CONTEXT_NAMES.indexOf(name) == -1) { 1.385 + return context; 1.386 + } 1.387 + // Repeated calls to 'getContext' return the same instance, no need to 1.388 + // instrument everything again. 1.389 + if (observer.for(context)) { 1.390 + return context; 1.391 + } 1.392 + 1.393 + // Create a separate state storage for this context. 1.394 + observer.registerContextForWindow(id, context); 1.395 + 1.396 + // Link our observer to the new WebGL context methods. 1.397 + for (let { timing, callback, functions } of self._methods) { 1.398 + for (let func of functions) { 1.399 + self._instrument(observer, context, func, callback, timing); 1.400 + } 1.401 + } 1.402 + 1.403 + // Return the decorated context back to the content consumer, which 1.404 + // will continue using it normally. 1.405 + return context; 1.406 + }; 1.407 + }, 1.408 + 1.409 + /** 1.410 + * Overrides a specific method in a HTMLCanvasElement context. 1.411 + * 1.412 + * @param WebGLObserver observer 1.413 + * The observer watching function calls in the context. 1.414 + * @param WebGLRenderingContext context 1.415 + * The targeted WebGL context instance. 1.416 + * @param string funcName 1.417 + * The function to override. 1.418 + * @param array callbackName [optional] 1.419 + * The two callback function names in the observer, corresponding to 1.420 + * the "before" and "after" invocation times. If unspecified, they will 1.421 + * default to the name of the function to override. 1.422 + * @param number timing [optional] 1.423 + * When to issue the callback in relation to the actual context 1.424 + * function call. Availalble values are -1 for "before" (default) 1.425 + * 1 for "after" and 0 for "before and after". 1.426 + */ 1.427 + _instrument: function(observer, context, funcName, callbackName = [], timing = -1) { 1.428 + let { cache, proxy } = observer.for(context); 1.429 + let originalFunc = context[funcName]; 1.430 + let beforeFuncName = callbackName[0] || funcName; 1.431 + let afterFuncName = callbackName[1] || callbackName[0] || funcName; 1.432 + 1.433 + context[funcName] = function(...glArgs) { 1.434 + if (timing <= 0 && !observer.suppressHandlers) { 1.435 + let glBreak = observer[beforeFuncName](glArgs, cache, proxy); 1.436 + if (glBreak) return undefined; 1.437 + } 1.438 + 1.439 + let glResult = originalFunc.apply(this, glArgs); 1.440 + 1.441 + if (timing >= 0 && !observer.suppressHandlers) { 1.442 + let glBreak = observer[afterFuncName](glArgs, glResult, cache, proxy); 1.443 + if (glBreak) return undefined; 1.444 + } 1.445 + 1.446 + return glResult; 1.447 + }; 1.448 + }, 1.449 + 1.450 + /** 1.451 + * Override mappings for WebGL methods. 1.452 + */ 1.453 + _methods: [{ 1.454 + timing: 1, // after 1.455 + functions: [ 1.456 + "linkProgram", "getAttribLocation", "getUniformLocation" 1.457 + ] 1.458 + }, { 1.459 + timing: -1, // before 1.460 + callback: [ 1.461 + "toggleVertexAttribArray" 1.462 + ], 1.463 + functions: [ 1.464 + "enableVertexAttribArray", "disableVertexAttribArray" 1.465 + ] 1.466 + }, { 1.467 + timing: -1, // before 1.468 + callback: [ 1.469 + "attribute_" 1.470 + ], 1.471 + functions: [ 1.472 + "vertexAttrib1f", "vertexAttrib2f", "vertexAttrib3f", "vertexAttrib4f", 1.473 + "vertexAttrib1fv", "vertexAttrib2fv", "vertexAttrib3fv", "vertexAttrib4fv", 1.474 + "vertexAttribPointer" 1.475 + ] 1.476 + }, { 1.477 + timing: -1, // before 1.478 + callback: [ 1.479 + "uniform_" 1.480 + ], 1.481 + functions: [ 1.482 + "uniform1i", "uniform2i", "uniform3i", "uniform4i", 1.483 + "uniform1f", "uniform2f", "uniform3f", "uniform4f", 1.484 + "uniform1iv", "uniform2iv", "uniform3iv", "uniform4iv", 1.485 + "uniform1fv", "uniform2fv", "uniform3fv", "uniform4fv", 1.486 + "uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv" 1.487 + ] 1.488 + }, { 1.489 + timing: -1, // before 1.490 + functions: [ 1.491 + "useProgram", "enable", "disable", "blendColor", 1.492 + "blendEquation", "blendEquationSeparate", 1.493 + "blendFunc", "blendFuncSeparate" 1.494 + ] 1.495 + }, { 1.496 + timing: 0, // before and after 1.497 + callback: [ 1.498 + "beforeDraw_", "afterDraw_" 1.499 + ], 1.500 + functions: [ 1.501 + "drawArrays", "drawElements" 1.502 + ] 1.503 + }] 1.504 + // TODO: It'd be a good idea to handle other functions as well: 1.505 + // - getActiveUniform 1.506 + // - getUniform 1.507 + // - getActiveAttrib 1.508 + // - getVertexAttrib 1.509 +}; 1.510 + 1.511 +/** 1.512 + * An observer that captures a WebGL context's method calls. 1.513 + */ 1.514 +function WebGLObserver() { 1.515 + this._contexts = new Map(); 1.516 +} 1.517 + 1.518 +WebGLObserver.prototype = { 1.519 + _contexts: null, 1.520 + 1.521 + /** 1.522 + * Creates a WebGLCache and a WebGLProxy for the specified window and context. 1.523 + * 1.524 + * @param number id 1.525 + * The id of the window containing the WebGL context. 1.526 + * @param WebGLRenderingContext context 1.527 + * The WebGL context used in the cache and proxy instances. 1.528 + */ 1.529 + registerContextForWindow: function(id, context) { 1.530 + let cache = new WebGLCache(id, context); 1.531 + let proxy = new WebGLProxy(id, context, cache, this); 1.532 + cache.refreshState(proxy); 1.533 + 1.534 + this._contexts.set(context, { 1.535 + ownerWindow: id, 1.536 + cache: cache, 1.537 + proxy: proxy 1.538 + }); 1.539 + }, 1.540 + 1.541 + /** 1.542 + * Removes all WebGLCache and WebGLProxy instances for a particular window. 1.543 + * 1.544 + * @param number id 1.545 + * The id of the window containing the WebGL context. 1.546 + */ 1.547 + unregisterContextsForWindow: function(id) { 1.548 + removeFromMap(this._contexts, e => e.ownerWindow == id); 1.549 + }, 1.550 + 1.551 + /** 1.552 + * Gets the WebGLCache and WebGLProxy instances for a particular context. 1.553 + * 1.554 + * @param WebGLRenderingContext context 1.555 + * The WebGL context used in the cache and proxy instances. 1.556 + * @return object 1.557 + * An object containing the corresponding { cache, proxy } instances. 1.558 + */ 1.559 + for: function(context) { 1.560 + return this._contexts.get(context); 1.561 + }, 1.562 + 1.563 + /** 1.564 + * Set this flag to true to stop observing any context function calls. 1.565 + */ 1.566 + suppressHandlers: false, 1.567 + 1.568 + /** 1.569 + * Called immediately *after* 'linkProgram' is requested in the context. 1.570 + * 1.571 + * @param array glArgs 1.572 + * Overridable arguments with which the function is called. 1.573 + * @param void glResult 1.574 + * The returned value of the original function call. 1.575 + * @param WebGLCache cache 1.576 + * The state storage for the WebGL context initiating this call. 1.577 + * @param WebGLProxy proxy 1.578 + * The proxy methods for the WebGL context initiating this call. 1.579 + */ 1.580 + linkProgram: function(glArgs, glResult, cache, proxy) { 1.581 + let program = glArgs[0]; 1.582 + let shaders = proxy.getAttachedShaders(program); 1.583 + cache.addProgram(program, PROGRAM_DEFAULT_TRAITS); 1.584 + emit(this, "program-linked", program, shaders, cache, proxy); 1.585 + }, 1.586 + 1.587 + /** 1.588 + * Called immediately *after* 'getAttribLocation' is requested in the context. 1.589 + * 1.590 + * @param array glArgs 1.591 + * Overridable arguments with which the function is called. 1.592 + * @param GLint glResult 1.593 + * The returned value of the original function call. 1.594 + * @param WebGLCache cache 1.595 + * The state storage for the WebGL context initiating this call. 1.596 + */ 1.597 + getAttribLocation: function(glArgs, glResult, cache) { 1.598 + // Make sure the attribute's value is legal before caching. 1.599 + if (glResult < 0) { 1.600 + return; 1.601 + } 1.602 + let [program, name] = glArgs; 1.603 + cache.addAttribute(program, name, glResult); 1.604 + }, 1.605 + 1.606 + /** 1.607 + * Called immediately *after* 'getUniformLocation' is requested in the context. 1.608 + * 1.609 + * @param array glArgs 1.610 + * Overridable arguments with which the function is called. 1.611 + * @param WebGLUniformLocation glResult 1.612 + * The returned value of the original function call. 1.613 + * @param WebGLCache cache 1.614 + * The state storage for the WebGL context initiating this call. 1.615 + */ 1.616 + getUniformLocation: function(glArgs, glResult, cache) { 1.617 + // Make sure the uniform's value is legal before caching. 1.618 + if (!glResult) { 1.619 + return; 1.620 + } 1.621 + let [program, name] = glArgs; 1.622 + cache.addUniform(program, name, glResult); 1.623 + }, 1.624 + 1.625 + /** 1.626 + * Called immediately *before* 'enableVertexAttribArray' or 1.627 + * 'disableVertexAttribArray'is requested in the context. 1.628 + * 1.629 + * @param array glArgs 1.630 + * Overridable arguments with which the function is called. 1.631 + * @param WebGLCache cache 1.632 + * The state storage for the WebGL context initiating this call. 1.633 + */ 1.634 + toggleVertexAttribArray: function(glArgs, cache) { 1.635 + glArgs[0] = cache.getCurrentAttributeLocation(glArgs[0]); 1.636 + return glArgs[0] < 0; // Return true to break original function call. 1.637 + }, 1.638 + 1.639 + /** 1.640 + * Called immediately *before* 'attribute_' is requested in the context. 1.641 + * 1.642 + * @param array glArgs 1.643 + * Overridable arguments with which the function is called. 1.644 + * @param WebGLCache cache 1.645 + * The state storage for the WebGL context initiating this call. 1.646 + */ 1.647 + attribute_: function(glArgs, cache) { 1.648 + glArgs[0] = cache.getCurrentAttributeLocation(glArgs[0]); 1.649 + return glArgs[0] < 0; // Return true to break original function call. 1.650 + }, 1.651 + 1.652 + /** 1.653 + * Called immediately *before* 'uniform_' is requested in the context. 1.654 + * 1.655 + * @param array glArgs 1.656 + * Overridable arguments with which the function is called. 1.657 + * @param WebGLCache cache 1.658 + * The state storage for the WebGL context initiating this call. 1.659 + */ 1.660 + uniform_: function(glArgs, cache) { 1.661 + glArgs[0] = cache.getCurrentUniformLocation(glArgs[0]); 1.662 + return !glArgs[0]; // Return true to break original function call. 1.663 + }, 1.664 + 1.665 + /** 1.666 + * Called immediately *before* 'useProgram' is requested in the context. 1.667 + * 1.668 + * @param array glArgs 1.669 + * Overridable arguments with which the function is called. 1.670 + * @param WebGLCache cache 1.671 + * The state storage for the WebGL context initiating this call. 1.672 + */ 1.673 + useProgram: function(glArgs, cache) { 1.674 + // Manually keeping a cache and not using gl.getParameter(CURRENT_PROGRAM) 1.675 + // because gl.get* functions are slow as potatoes. 1.676 + cache.currentProgram = glArgs[0]; 1.677 + }, 1.678 + 1.679 + /** 1.680 + * Called immediately *before* 'enable' is requested in the context. 1.681 + * 1.682 + * @param array glArgs 1.683 + * Overridable arguments with which the function is called. 1.684 + * @param WebGLCache cache 1.685 + * The state storage for the WebGL context initiating this call. 1.686 + */ 1.687 + enable: function(glArgs, cache) { 1.688 + cache.currentState[glArgs[0]] = true; 1.689 + }, 1.690 + 1.691 + /** 1.692 + * Called immediately *before* 'disable' is requested in the context. 1.693 + * 1.694 + * @param array glArgs 1.695 + * Overridable arguments with which the function is called. 1.696 + * @param WebGLCache cache 1.697 + * The state storage for the WebGL context initiating this call. 1.698 + */ 1.699 + disable: function(glArgs, cache) { 1.700 + cache.currentState[glArgs[0]] = false; 1.701 + }, 1.702 + 1.703 + /** 1.704 + * Called immediately *before* 'blendColor' is requested in the context. 1.705 + * 1.706 + * @param array glArgs 1.707 + * Overridable arguments with which the function is called. 1.708 + * @param WebGLCache cache 1.709 + * The state storage for the WebGL context initiating this call. 1.710 + */ 1.711 + blendColor: function(glArgs, cache) { 1.712 + let blendColor = cache.currentState.blendColor; 1.713 + blendColor[0] = glArgs[0]; 1.714 + blendColor[1] = glArgs[1]; 1.715 + blendColor[2] = glArgs[2]; 1.716 + blendColor[3] = glArgs[3]; 1.717 + }, 1.718 + 1.719 + /** 1.720 + * Called immediately *before* 'blendEquation' is requested in the context. 1.721 + * 1.722 + * @param array glArgs 1.723 + * Overridable arguments with which the function is called. 1.724 + * @param WebGLCache cache 1.725 + * The state storage for the WebGL context initiating this call. 1.726 + */ 1.727 + blendEquation: function(glArgs, cache) { 1.728 + let state = cache.currentState; 1.729 + state.blendEquationRgb = state.blendEquationAlpha = glArgs[0]; 1.730 + }, 1.731 + 1.732 + /** 1.733 + * Called immediately *before* 'blendEquationSeparate' is requested in the context. 1.734 + * 1.735 + * @param array glArgs 1.736 + * Overridable arguments with which the function is called. 1.737 + * @param WebGLCache cache 1.738 + * The state storage for the WebGL context initiating this call. 1.739 + */ 1.740 + blendEquationSeparate: function(glArgs, cache) { 1.741 + let state = cache.currentState; 1.742 + state.blendEquationRgb = glArgs[0]; 1.743 + state.blendEquationAlpha = glArgs[1]; 1.744 + }, 1.745 + 1.746 + /** 1.747 + * Called immediately *before* 'blendFunc' is requested in the context. 1.748 + * 1.749 + * @param array glArgs 1.750 + * Overridable arguments with which the function is called. 1.751 + * @param WebGLCache cache 1.752 + * The state storage for the WebGL context initiating this call. 1.753 + */ 1.754 + blendFunc: function(glArgs, cache) { 1.755 + let state = cache.currentState; 1.756 + state.blendSrcRgb = state.blendSrcAlpha = glArgs[0]; 1.757 + state.blendDstRgb = state.blendDstAlpha = glArgs[1]; 1.758 + }, 1.759 + 1.760 + /** 1.761 + * Called immediately *before* 'blendFuncSeparate' is requested in the context. 1.762 + * 1.763 + * @param array glArgs 1.764 + * Overridable arguments with which the function is called. 1.765 + * @param WebGLCache cache 1.766 + * The state storage for the WebGL context initiating this call. 1.767 + */ 1.768 + blendFuncSeparate: function(glArgs, cache) { 1.769 + let state = cache.currentState; 1.770 + state.blendSrcRgb = glArgs[0]; 1.771 + state.blendDstRgb = glArgs[1]; 1.772 + state.blendSrcAlpha = glArgs[2]; 1.773 + state.blendDstAlpha = glArgs[3]; 1.774 + }, 1.775 + 1.776 + /** 1.777 + * Called immediately *before* 'drawArrays' or 'drawElements' is requested 1.778 + * in the context. 1.779 + * 1.780 + * @param array glArgs 1.781 + * Overridable arguments with which the function is called. 1.782 + * @param WebGLCache cache 1.783 + * The state storage for the WebGL context initiating this call. 1.784 + * @param WebGLProxy proxy 1.785 + * The proxy methods for the WebGL context initiating this call. 1.786 + */ 1.787 + beforeDraw_: function(glArgs, cache, proxy) { 1.788 + let traits = cache.currentProgramTraits; 1.789 + 1.790 + // Handle program blackboxing. 1.791 + if (traits & PROGRAM_BLACKBOX_TRAIT) { 1.792 + return true; // Return true to break original function call. 1.793 + } 1.794 + // Handle program highlighting. 1.795 + if (traits & PROGRAM_HIGHLIGHT_TRAIT) { 1.796 + proxy.enableHighlighting(); 1.797 + } 1.798 + 1.799 + return false; 1.800 + }, 1.801 + 1.802 + /** 1.803 + * Called immediately *after* 'drawArrays' or 'drawElements' is requested 1.804 + * in the context. 1.805 + * 1.806 + * @param array glArgs 1.807 + * Overridable arguments with which the function is called. 1.808 + * @param void glResult 1.809 + * The returned value of the original function call. 1.810 + * @param WebGLCache cache 1.811 + * The state storage for the WebGL context initiating this call. 1.812 + * @param WebGLProxy proxy 1.813 + * The proxy methods for the WebGL context initiating this call. 1.814 + */ 1.815 + afterDraw_: function(glArgs, glResult, cache, proxy) { 1.816 + let traits = cache.currentProgramTraits; 1.817 + 1.818 + // Handle program highlighting. 1.819 + if (traits & PROGRAM_HIGHLIGHT_TRAIT) { 1.820 + proxy.disableHighlighting(); 1.821 + } 1.822 + } 1.823 +}; 1.824 + 1.825 +/** 1.826 + * A mechanism for storing a single WebGL context's state, programs, shaders, 1.827 + * attributes or uniforms. 1.828 + * 1.829 + * @param number id 1.830 + * The id of the window containing the WebGL context. 1.831 + * @param WebGLRenderingContext context 1.832 + * The WebGL context for which the state is stored. 1.833 + */ 1.834 +function WebGLCache(id, context) { 1.835 + this._id = id; 1.836 + this._gl = context; 1.837 + this._programs = new Map(); 1.838 + this.currentState = {}; 1.839 +} 1.840 + 1.841 +WebGLCache.prototype = { 1.842 + _id: 0, 1.843 + _gl: null, 1.844 + _programs: null, 1.845 + _currentProgramInfo: null, 1.846 + _currentAttributesMap: null, 1.847 + _currentUniformsMap: null, 1.848 + 1.849 + get ownerWindow() this._id, 1.850 + get ownerContext() this._gl, 1.851 + 1.852 + /** 1.853 + * A collection of flags or properties representing the context's state. 1.854 + * Implemented as an object hash and not a Map instance because keys are 1.855 + * always either strings or numbers. 1.856 + */ 1.857 + currentState: null, 1.858 + 1.859 + /** 1.860 + * Populates the current state with values retrieved from the context. 1.861 + * 1.862 + * @param WebGLProxy proxy 1.863 + * The proxy methods for the WebGL context owning the state. 1.864 + */ 1.865 + refreshState: function(proxy) { 1.866 + let gl = this._gl; 1.867 + let s = this.currentState; 1.868 + 1.869 + // Populate only with the necessary parameters. Not all default WebGL 1.870 + // state values are required. 1.871 + s[gl.BLEND] = proxy.isEnabled("BLEND"); 1.872 + s.blendColor = proxy.getParameter("BLEND_COLOR"); 1.873 + s.blendEquationRgb = proxy.getParameter("BLEND_EQUATION_RGB"); 1.874 + s.blendEquationAlpha = proxy.getParameter("BLEND_EQUATION_ALPHA"); 1.875 + s.blendSrcRgb = proxy.getParameter("BLEND_SRC_RGB"); 1.876 + s.blendSrcAlpha = proxy.getParameter("BLEND_SRC_ALPHA"); 1.877 + s.blendDstRgb = proxy.getParameter("BLEND_DST_RGB"); 1.878 + s.blendDstAlpha = proxy.getParameter("BLEND_DST_ALPHA"); 1.879 + }, 1.880 + 1.881 + /** 1.882 + * Adds a program to the cache. 1.883 + * 1.884 + * @param WebGLProgram program 1.885 + * The shader for which the traits are to be cached. 1.886 + * @param number traits 1.887 + * A default properties mask set for the program. 1.888 + */ 1.889 + addProgram: function(program, traits) { 1.890 + this._programs.set(program, { 1.891 + traits: traits, 1.892 + attributes: [], // keys are GLints (numbers) 1.893 + uniforms: new Map() // keys are WebGLUniformLocations (objects) 1.894 + }); 1.895 + }, 1.896 + 1.897 + /** 1.898 + * Adds a specific trait to a program. The effect of such properties is 1.899 + * determined by the consumer of this cache. 1.900 + * 1.901 + * @param WebGLProgram program 1.902 + * The program to add the trait to. 1.903 + * @param number trait 1.904 + * The property added to the program. 1.905 + */ 1.906 + setProgramTrait: function(program, trait) { 1.907 + this._programs.get(program).traits |= trait; 1.908 + }, 1.909 + 1.910 + /** 1.911 + * Removes a specific trait from a program. 1.912 + * 1.913 + * @param WebGLProgram program 1.914 + * The program to remove the trait from. 1.915 + * @param number trait 1.916 + * The property removed from the program. 1.917 + */ 1.918 + unsetProgramTrait: function(program, trait) { 1.919 + this._programs.get(program).traits &= ~trait; 1.920 + }, 1.921 + 1.922 + /** 1.923 + * Sets the currently used program in the context. 1.924 + * @param WebGLProgram program 1.925 + */ 1.926 + set currentProgram(program) { 1.927 + let programInfo = this._programs.get(program); 1.928 + if (programInfo == null) { 1.929 + return; 1.930 + } 1.931 + this._currentProgramInfo = programInfo; 1.932 + this._currentAttributesMap = programInfo.attributes; 1.933 + this._currentUniformsMap = programInfo.uniforms; 1.934 + }, 1.935 + 1.936 + /** 1.937 + * Gets the traits for the currently used program. 1.938 + * @return number 1.939 + */ 1.940 + get currentProgramTraits() { 1.941 + return this._currentProgramInfo.traits; 1.942 + }, 1.943 + 1.944 + /** 1.945 + * Adds an attribute to the cache. 1.946 + * 1.947 + * @param WebGLProgram program 1.948 + * The program for which the attribute is bound. 1.949 + * @param string name 1.950 + * The attribute name. 1.951 + * @param GLint value 1.952 + * The attribute value. 1.953 + */ 1.954 + addAttribute: function(program, name, value) { 1.955 + this._programs.get(program).attributes[value] = { 1.956 + name: name, 1.957 + value: value 1.958 + }; 1.959 + }, 1.960 + 1.961 + /** 1.962 + * Adds a uniform to the cache. 1.963 + * 1.964 + * @param WebGLProgram program 1.965 + * The program for which the uniform is bound. 1.966 + * @param string name 1.967 + * The uniform name. 1.968 + * @param WebGLUniformLocation value 1.969 + * The uniform value. 1.970 + */ 1.971 + addUniform: function(program, name, value) { 1.972 + this._programs.get(program).uniforms.set(new XPCNativeWrapper(value), { 1.973 + name: name, 1.974 + value: value 1.975 + }); 1.976 + }, 1.977 + 1.978 + /** 1.979 + * Updates the attribute locations for a specific program. 1.980 + * This is necessary, for example, when the shader is relinked and all the 1.981 + * attribute locations become obsolete. 1.982 + * 1.983 + * @param WebGLProgram program 1.984 + * The program for which the attributes need updating. 1.985 + */ 1.986 + updateAttributesForProgram: function(program) { 1.987 + let attributes = this._programs.get(program).attributes; 1.988 + for (let attribute of attributes) { 1.989 + attribute.value = this._gl.getAttribLocation(program, attribute.name); 1.990 + } 1.991 + }, 1.992 + 1.993 + /** 1.994 + * Updates the uniform locations for a specific program. 1.995 + * This is necessary, for example, when the shader is relinked and all the 1.996 + * uniform locations become obsolete. 1.997 + * 1.998 + * @param WebGLProgram program 1.999 + * The program for which the uniforms need updating. 1.1000 + */ 1.1001 + updateUniformsForProgram: function(program) { 1.1002 + let uniforms = this._programs.get(program).uniforms; 1.1003 + for (let [, uniform] of uniforms) { 1.1004 + uniform.value = this._gl.getUniformLocation(program, uniform.name); 1.1005 + } 1.1006 + }, 1.1007 + 1.1008 + /** 1.1009 + * Gets the actual attribute location in a specific program. 1.1010 + * When relinked, all the attribute locations become obsolete and are updated 1.1011 + * in the cache. This method returns the (current) real attribute location. 1.1012 + * 1.1013 + * @param GLint initialValue 1.1014 + * The initial attribute value. 1.1015 + * @return GLint 1.1016 + * The current attribute value, or the initial value if it's already 1.1017 + * up to date with its corresponding program. 1.1018 + */ 1.1019 + getCurrentAttributeLocation: function(initialValue) { 1.1020 + let attributes = this._currentAttributesMap; 1.1021 + let currentInfo = attributes ? attributes[initialValue] : null; 1.1022 + return currentInfo ? currentInfo.value : initialValue; 1.1023 + }, 1.1024 + 1.1025 + /** 1.1026 + * Gets the actual uniform location in a specific program. 1.1027 + * When relinked, all the uniform locations become obsolete and are updated 1.1028 + * in the cache. This method returns the (current) real uniform location. 1.1029 + * 1.1030 + * @param WebGLUniformLocation initialValue 1.1031 + * The initial uniform value. 1.1032 + * @return WebGLUniformLocation 1.1033 + * The current uniform value, or the initial value if it's already 1.1034 + * up to date with its corresponding program. 1.1035 + */ 1.1036 + getCurrentUniformLocation: function(initialValue) { 1.1037 + let uniforms = this._currentUniformsMap; 1.1038 + let currentInfo = uniforms ? uniforms.get(initialValue) : null; 1.1039 + return currentInfo ? currentInfo.value : initialValue; 1.1040 + } 1.1041 +}; 1.1042 + 1.1043 +/** 1.1044 + * A mechanism for injecting or qureying state into/from a single WebGL context. 1.1045 + * 1.1046 + * Any interaction with a WebGL context should go through this proxy. 1.1047 + * Otherwise, the corresponding observer would register the calls as coming 1.1048 + * from content, which is usually not desirable. Infinite call stacks are bad. 1.1049 + * 1.1050 + * @param number id 1.1051 + * The id of the window containing the WebGL context. 1.1052 + * @param WebGLRenderingContext context 1.1053 + * The WebGL context used for the proxy methods. 1.1054 + * @param WebGLCache cache 1.1055 + * The state storage for the corresponding context. 1.1056 + * @param WebGLObserver observer 1.1057 + * The observer watching function calls in the corresponding context. 1.1058 + */ 1.1059 +function WebGLProxy(id, context, cache, observer) { 1.1060 + this._id = id; 1.1061 + this._gl = context; 1.1062 + this._cache = cache; 1.1063 + this._observer = observer; 1.1064 + 1.1065 + let exports = [ 1.1066 + "isEnabled", 1.1067 + "getParameter", 1.1068 + "getAttachedShaders", 1.1069 + "getShaderSource", 1.1070 + "getShaderOfType", 1.1071 + "compileShader", 1.1072 + "enableHighlighting", 1.1073 + "disableHighlighting" 1.1074 + ]; 1.1075 + exports.forEach(e => this[e] = (...args) => this._call(e, args)); 1.1076 +} 1.1077 + 1.1078 +WebGLProxy.prototype = { 1.1079 + _id: 0, 1.1080 + _gl: null, 1.1081 + _cache: null, 1.1082 + _observer: null, 1.1083 + 1.1084 + get ownerWindow() this._id, 1.1085 + get ownerContext() this._gl, 1.1086 + 1.1087 + /** 1.1088 + * Test whether a WebGL capability is enabled. 1.1089 + * 1.1090 + * @param string name 1.1091 + * The WebGL capability name, for example "BLEND". 1.1092 + * @return boolean 1.1093 + * True if enabled, false otherwise. 1.1094 + */ 1.1095 + _isEnabled: function(name) { 1.1096 + return this._gl.isEnabled(this._gl[name]); 1.1097 + }, 1.1098 + 1.1099 + /** 1.1100 + * Returns the value for the specified WebGL parameter name. 1.1101 + * 1.1102 + * @param string name 1.1103 + * The WebGL parameter name, for example "BLEND_COLOR". 1.1104 + * @return any 1.1105 + * The corresponding parameter's value. 1.1106 + */ 1.1107 + _getParameter: function(name) { 1.1108 + return this._gl.getParameter(this._gl[name]); 1.1109 + }, 1.1110 + 1.1111 + /** 1.1112 + * Returns the renderbuffer property value for the specified WebGL parameter. 1.1113 + * If no renderbuffer binding is available, null is returned. 1.1114 + * 1.1115 + * @param string name 1.1116 + * The WebGL parameter name, for example "BLEND_COLOR". 1.1117 + * @return any 1.1118 + * The corresponding parameter's value. 1.1119 + */ 1.1120 + _getRenderbufferParameter: function(name) { 1.1121 + if (!this._getParameter("RENDERBUFFER_BINDING")) { 1.1122 + return null; 1.1123 + } 1.1124 + let gl = this._gl; 1.1125 + return gl.getRenderbufferParameter(gl.RENDERBUFFER, gl[name]); 1.1126 + }, 1.1127 + 1.1128 + /** 1.1129 + * Returns the framebuffer property value for the specified WebGL parameter. 1.1130 + * If no framebuffer binding is available, null is returned. 1.1131 + * 1.1132 + * @param string type 1.1133 + * The framebuffer object attachment point, for example "COLOR_ATTACHMENT0". 1.1134 + * @param string name 1.1135 + * The WebGL parameter name, for example "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME". 1.1136 + * If unspecified, defaults to "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE". 1.1137 + * @return any 1.1138 + * The corresponding parameter's value. 1.1139 + */ 1.1140 + _getFramebufferAttachmentParameter: function(type, name = "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE") { 1.1141 + if (!this._getParameter("FRAMEBUFFER_BINDING")) { 1.1142 + return null; 1.1143 + } 1.1144 + let gl = this._gl; 1.1145 + return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl[type], gl[name]); 1.1146 + }, 1.1147 + 1.1148 + /** 1.1149 + * Returns the shader objects attached to a program object. 1.1150 + * 1.1151 + * @param WebGLProgram program 1.1152 + * The program for which to retrieve the attached shaders. 1.1153 + * @return array 1.1154 + * The attached vertex and fragment shaders. 1.1155 + */ 1.1156 + _getAttachedShaders: function(program) { 1.1157 + return this._gl.getAttachedShaders(program); 1.1158 + }, 1.1159 + 1.1160 + /** 1.1161 + * Returns the source code string from a shader object. 1.1162 + * 1.1163 + * @param WebGLShader shader 1.1164 + * The shader for which to retrieve the source code. 1.1165 + * @return string 1.1166 + * The shader's source code. 1.1167 + */ 1.1168 + _getShaderSource: function(shader) { 1.1169 + return this._gl.getShaderSource(shader); 1.1170 + }, 1.1171 + 1.1172 + /** 1.1173 + * Finds a shader of the specified type in a list. 1.1174 + * 1.1175 + * @param WebGLShader[] shaders 1.1176 + * The shaders for which to check the type. 1.1177 + * @param string type 1.1178 + * Either "vertex" or "fragment". 1.1179 + * @return WebGLShader | null 1.1180 + * The shader of the specified type, or null if nothing is found. 1.1181 + */ 1.1182 + _getShaderOfType: function(shaders, type) { 1.1183 + let gl = this._gl; 1.1184 + let shaderTypeEnum = { 1.1185 + vertex: gl.VERTEX_SHADER, 1.1186 + fragment: gl.FRAGMENT_SHADER 1.1187 + }[type]; 1.1188 + 1.1189 + for (let shader of shaders) { 1.1190 + if (gl.getShaderParameter(shader, gl.SHADER_TYPE) == shaderTypeEnum) { 1.1191 + return shader; 1.1192 + } 1.1193 + } 1.1194 + return null; 1.1195 + }, 1.1196 + 1.1197 + /** 1.1198 + * Changes a shader's source code and relinks the respective program. 1.1199 + * 1.1200 + * @param WebGLProgram program 1.1201 + * The program who's linked shader is to be modified. 1.1202 + * @param WebGLShader shader 1.1203 + * The shader to be modified. 1.1204 + * @param string text 1.1205 + * The new shader source code. 1.1206 + * @return object 1.1207 + * An object containing the compilation and linking status. 1.1208 + */ 1.1209 + _compileShader: function(program, shader, text) { 1.1210 + let gl = this._gl; 1.1211 + gl.shaderSource(shader, text); 1.1212 + gl.compileShader(shader); 1.1213 + gl.linkProgram(program); 1.1214 + 1.1215 + let error = { compile: "", link: "" }; 1.1216 + 1.1217 + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 1.1218 + error.compile = gl.getShaderInfoLog(shader); 1.1219 + } 1.1220 + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { 1.1221 + error.link = gl.getShaderInfoLog(shader); 1.1222 + } 1.1223 + 1.1224 + this._cache.updateAttributesForProgram(program); 1.1225 + this._cache.updateUniformsForProgram(program); 1.1226 + 1.1227 + return error; 1.1228 + }, 1.1229 + 1.1230 + /** 1.1231 + * Enables color blending based on the geometry highlight tint. 1.1232 + */ 1.1233 + _enableHighlighting: function() { 1.1234 + let gl = this._gl; 1.1235 + 1.1236 + // Avoid changing the blending params when "rendering to texture". 1.1237 + 1.1238 + // Check drawing to a custom framebuffer bound to the default renderbuffer. 1.1239 + let hasFramebuffer = this._getParameter("FRAMEBUFFER_BINDING"); 1.1240 + let hasRenderbuffer = this._getParameter("RENDERBUFFER_BINDING"); 1.1241 + if (hasFramebuffer && !hasRenderbuffer) { 1.1242 + return; 1.1243 + } 1.1244 + 1.1245 + // Check drawing to a depth or stencil component of the framebuffer. 1.1246 + let writesDepth = this._getFramebufferAttachmentParameter("DEPTH_ATTACHMENT"); 1.1247 + let writesStencil = this._getFramebufferAttachmentParameter("STENCIL_ATTACHMENT"); 1.1248 + if (writesDepth || writesStencil) { 1.1249 + return; 1.1250 + } 1.1251 + 1.1252 + // Non-premultiplied alpha blending based on a predefined constant color. 1.1253 + // Simply using gl.colorMask won't work, because we want non-tinted colors 1.1254 + // to be drawn as black, not ignored. 1.1255 + gl.enable(gl.BLEND); 1.1256 + gl.blendColor.apply(gl, this.highlightTint); 1.1257 + gl.blendEquation(gl.FUNC_ADD); 1.1258 + gl.blendFunc(gl.CONSTANT_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.CONSTANT_COLOR, gl.ZERO); 1.1259 + this.wasHighlighting = true; 1.1260 + }, 1.1261 + 1.1262 + /** 1.1263 + * Disables color blending based on the geometry highlight tint, by 1.1264 + * reverting the corresponding params back to their original values. 1.1265 + */ 1.1266 + _disableHighlighting: function() { 1.1267 + let gl = this._gl; 1.1268 + let s = this._cache.currentState; 1.1269 + 1.1270 + gl[s[gl.BLEND] ? "enable" : "disable"](gl.BLEND); 1.1271 + gl.blendColor.apply(gl, s.blendColor); 1.1272 + gl.blendEquationSeparate(s.blendEquationRgb, s.blendEquationAlpha); 1.1273 + gl.blendFuncSeparate(s.blendSrcRgb, s.blendDstRgb, s.blendSrcAlpha, s.blendDstAlpha); 1.1274 + }, 1.1275 + 1.1276 + /** 1.1277 + * The color tint used for highlighting geometry. 1.1278 + * @see _enableHighlighting and _disableHighlighting. 1.1279 + */ 1.1280 + highlightTint: [0, 0, 0, 0], 1.1281 + 1.1282 + /** 1.1283 + * Executes a function in this object. 1.1284 + * 1.1285 + * This method makes sure that any handlers in the context observer are 1.1286 + * suppressed, hence stopping observing any context function calls. 1.1287 + * 1.1288 + * @param string funcName 1.1289 + * The function to call. 1.1290 + * @param array args 1.1291 + * An array of arguments. 1.1292 + * @return any 1.1293 + * The called function result. 1.1294 + */ 1.1295 + _call: function(funcName, args) { 1.1296 + let prevState = this._observer.suppressHandlers; 1.1297 + 1.1298 + this._observer.suppressHandlers = true; 1.1299 + let result = this["_" + funcName].apply(this, args); 1.1300 + this._observer.suppressHandlers = prevState; 1.1301 + 1.1302 + return result; 1.1303 + } 1.1304 +}; 1.1305 + 1.1306 +// Utility functions. 1.1307 + 1.1308 +function removeFromMap(map, predicate) { 1.1309 + for (let [key, value] of map) { 1.1310 + if (predicate(value)) { 1.1311 + map.delete(key); 1.1312 + } 1.1313 + } 1.1314 +}; 1.1315 + 1.1316 +function removeFromArray(array, predicate) { 1.1317 + for (let value of array) { 1.1318 + if (predicate(value)) { 1.1319 + array.splice(array.indexOf(value), 1); 1.1320 + } 1.1321 + } 1.1322 +}