toolkit/devtools/server/actors/webgl.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 "use strict";
michael@0 5
michael@0 6 const {Cc, Ci, Cu, Cr} = require("chrome");
michael@0 7 const events = require("sdk/event/core");
michael@0 8 const protocol = require("devtools/server/protocol");
michael@0 9 const { ContentObserver } = require("devtools/content-observer");
michael@0 10
michael@0 11 const { on, once, off, emit } = events;
michael@0 12 const { method, Arg, Option, RetVal } = protocol;
michael@0 13
michael@0 14 const WEBGL_CONTEXT_NAMES = ["webgl", "experimental-webgl", "moz-webgl"];
michael@0 15
michael@0 16 // These traits are bit masks. Make sure they're powers of 2.
michael@0 17 const PROGRAM_DEFAULT_TRAITS = 0;
michael@0 18 const PROGRAM_BLACKBOX_TRAIT = 1;
michael@0 19 const PROGRAM_HIGHLIGHT_TRAIT = 2;
michael@0 20
michael@0 21 exports.register = function(handle) {
michael@0 22 handle.addTabActor(WebGLActor, "webglActor");
michael@0 23 }
michael@0 24
michael@0 25 exports.unregister = function(handle) {
michael@0 26 handle.removeTabActor(WebGLActor);
michael@0 27 }
michael@0 28
michael@0 29 /**
michael@0 30 * A WebGL Shader contributing to building a WebGL Program.
michael@0 31 * You can either retrieve, or compile the source of a shader, which will
michael@0 32 * automatically inflict the necessary changes to the WebGL state.
michael@0 33 */
michael@0 34 let ShaderActor = protocol.ActorClass({
michael@0 35 typeName: "gl-shader",
michael@0 36
michael@0 37 /**
michael@0 38 * Create the shader actor.
michael@0 39 *
michael@0 40 * @param DebuggerServerConnection conn
michael@0 41 * The server connection.
michael@0 42 * @param WebGLProgram program
michael@0 43 * The WebGL program being linked.
michael@0 44 * @param WebGLShader shader
michael@0 45 * The cooresponding vertex or fragment shader.
michael@0 46 * @param WebGLProxy proxy
michael@0 47 * The proxy methods for the WebGL context owning this shader.
michael@0 48 */
michael@0 49 initialize: function(conn, program, shader, proxy) {
michael@0 50 protocol.Actor.prototype.initialize.call(this, conn);
michael@0 51 this.program = program;
michael@0 52 this.shader = shader;
michael@0 53 this.text = proxy.getShaderSource(shader);
michael@0 54 this.linkedProxy = proxy;
michael@0 55 },
michael@0 56
michael@0 57 /**
michael@0 58 * Gets the source code for this shader.
michael@0 59 */
michael@0 60 getText: method(function() {
michael@0 61 return this.text;
michael@0 62 }, {
michael@0 63 response: { text: RetVal("string") }
michael@0 64 }),
michael@0 65
michael@0 66 /**
michael@0 67 * Sets and compiles new source code for this shader.
michael@0 68 */
michael@0 69 compile: method(function(text) {
michael@0 70 // Get the shader and corresponding program to change via the WebGL proxy.
michael@0 71 let { linkedProxy: proxy, shader, program } = this;
michael@0 72
michael@0 73 // Get the new shader source to inject.
michael@0 74 let oldText = this.text;
michael@0 75 let newText = text;
michael@0 76
michael@0 77 // Overwrite the shader's source.
michael@0 78 let error = proxy.compileShader(program, shader, this.text = newText);
michael@0 79
michael@0 80 // If something went wrong, revert to the previous shader.
michael@0 81 if (error.compile || error.link) {
michael@0 82 proxy.compileShader(program, shader, this.text = oldText);
michael@0 83 return error;
michael@0 84 }
michael@0 85 return undefined;
michael@0 86 }, {
michael@0 87 request: { text: Arg(0, "string") },
michael@0 88 response: { error: RetVal("nullable:json") }
michael@0 89 })
michael@0 90 });
michael@0 91
michael@0 92 /**
michael@0 93 * The corresponding Front object for the ShaderActor.
michael@0 94 */
michael@0 95 let ShaderFront = protocol.FrontClass(ShaderActor, {
michael@0 96 initialize: function(client, form) {
michael@0 97 protocol.Front.prototype.initialize.call(this, client, form);
michael@0 98 }
michael@0 99 });
michael@0 100
michael@0 101 /**
michael@0 102 * A WebGL program is composed (at the moment, analogue to OpenGL ES 2.0)
michael@0 103 * of two shaders: a vertex shader and a fragment shader.
michael@0 104 */
michael@0 105 let ProgramActor = protocol.ActorClass({
michael@0 106 typeName: "gl-program",
michael@0 107
michael@0 108 /**
michael@0 109 * Create the program actor.
michael@0 110 *
michael@0 111 * @param DebuggerServerConnection conn
michael@0 112 * The server connection.
michael@0 113 * @param WebGLProgram program
michael@0 114 * The WebGL program being linked.
michael@0 115 * @param WebGLShader[] shaders
michael@0 116 * The WebGL program's cooresponding vertex and fragment shaders.
michael@0 117 * @param WebGLCache cache
michael@0 118 * The state storage for the WebGL context owning this program.
michael@0 119 * @param WebGLProxy proxy
michael@0 120 * The proxy methods for the WebGL context owning this program.
michael@0 121 */
michael@0 122 initialize: function(conn, [program, shaders, cache, proxy]) {
michael@0 123 protocol.Actor.prototype.initialize.call(this, conn);
michael@0 124 this._shaderActorsCache = { vertex: null, fragment: null };
michael@0 125 this.program = program;
michael@0 126 this.shaders = shaders;
michael@0 127 this.linkedCache = cache;
michael@0 128 this.linkedProxy = proxy;
michael@0 129 },
michael@0 130
michael@0 131 get ownerWindow() this.linkedCache.ownerWindow,
michael@0 132 get ownerContext() this.linkedCache.ownerContext,
michael@0 133
michael@0 134 /**
michael@0 135 * Gets the vertex shader linked to this program. This method guarantees
michael@0 136 * a single actor instance per shader.
michael@0 137 */
michael@0 138 getVertexShader: method(function() {
michael@0 139 return this._getShaderActor("vertex");
michael@0 140 }, {
michael@0 141 response: { shader: RetVal("gl-shader") }
michael@0 142 }),
michael@0 143
michael@0 144 /**
michael@0 145 * Gets the fragment shader linked to this program. This method guarantees
michael@0 146 * a single actor instance per shader.
michael@0 147 */
michael@0 148 getFragmentShader: method(function() {
michael@0 149 return this._getShaderActor("fragment");
michael@0 150 }, {
michael@0 151 response: { shader: RetVal("gl-shader") }
michael@0 152 }),
michael@0 153
michael@0 154 /**
michael@0 155 * Highlights any geometry rendered using this program.
michael@0 156 */
michael@0 157 highlight: method(function(tint) {
michael@0 158 this.linkedProxy.highlightTint = tint;
michael@0 159 this.linkedCache.setProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT);
michael@0 160 }, {
michael@0 161 request: { tint: Arg(0, "array:number") },
michael@0 162 oneway: true
michael@0 163 }),
michael@0 164
michael@0 165 /**
michael@0 166 * Allows geometry to be rendered normally using this program.
michael@0 167 */
michael@0 168 unhighlight: method(function() {
michael@0 169 this.linkedCache.unsetProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT);
michael@0 170 }, {
michael@0 171 oneway: true
michael@0 172 }),
michael@0 173
michael@0 174 /**
michael@0 175 * Prevents any geometry from being rendered using this program.
michael@0 176 */
michael@0 177 blackbox: method(function() {
michael@0 178 this.linkedCache.setProgramTrait(this.program, PROGRAM_BLACKBOX_TRAIT);
michael@0 179 }, {
michael@0 180 oneway: true
michael@0 181 }),
michael@0 182
michael@0 183 /**
michael@0 184 * Allows geometry to be rendered using this program.
michael@0 185 */
michael@0 186 unblackbox: method(function() {
michael@0 187 this.linkedCache.unsetProgramTrait(this.program, PROGRAM_BLACKBOX_TRAIT);
michael@0 188 }, {
michael@0 189 oneway: true
michael@0 190 }),
michael@0 191
michael@0 192 /**
michael@0 193 * Returns a cached ShaderActor instance based on the required shader type.
michael@0 194 *
michael@0 195 * @param string type
michael@0 196 * Either "vertex" or "fragment".
michael@0 197 * @return ShaderActor
michael@0 198 * The respective shader actor instance.
michael@0 199 */
michael@0 200 _getShaderActor: function(type) {
michael@0 201 if (this._shaderActorsCache[type]) {
michael@0 202 return this._shaderActorsCache[type];
michael@0 203 }
michael@0 204 let proxy = this.linkedProxy;
michael@0 205 let shader = proxy.getShaderOfType(this.shaders, type);
michael@0 206 let shaderActor = new ShaderActor(this.conn, this.program, shader, proxy);
michael@0 207 return this._shaderActorsCache[type] = shaderActor;
michael@0 208 }
michael@0 209 });
michael@0 210
michael@0 211 /**
michael@0 212 * The corresponding Front object for the ProgramActor.
michael@0 213 */
michael@0 214 let ProgramFront = protocol.FrontClass(ProgramActor, {
michael@0 215 initialize: function(client, form) {
michael@0 216 protocol.Front.prototype.initialize.call(this, client, form);
michael@0 217 }
michael@0 218 });
michael@0 219
michael@0 220 /**
michael@0 221 * The WebGL Actor handles simple interaction with a WebGL context via a few
michael@0 222 * high-level methods. After instantiating this actor, you'll need to set it
michael@0 223 * up by calling setup().
michael@0 224 */
michael@0 225 let WebGLActor = exports.WebGLActor = protocol.ActorClass({
michael@0 226 typeName: "webgl",
michael@0 227 initialize: function(conn, tabActor) {
michael@0 228 protocol.Actor.prototype.initialize.call(this, conn);
michael@0 229 this.tabActor = tabActor;
michael@0 230 this._onGlobalCreated = this._onGlobalCreated.bind(this);
michael@0 231 this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
michael@0 232 this._onProgramLinked = this._onProgramLinked.bind(this);
michael@0 233 },
michael@0 234 destroy: function(conn) {
michael@0 235 protocol.Actor.prototype.destroy.call(this, conn);
michael@0 236 this.finalize();
michael@0 237 },
michael@0 238
michael@0 239 /**
michael@0 240 * Starts waiting for the current tab actor's document global to be
michael@0 241 * created, in order to instrument the Canvas context and become
michael@0 242 * aware of everything the content does WebGL-wise.
michael@0 243 *
michael@0 244 * See ContentObserver and WebGLInstrumenter for more details.
michael@0 245 */
michael@0 246 setup: method(function({ reload }) {
michael@0 247 if (this._initialized) {
michael@0 248 return;
michael@0 249 }
michael@0 250 this._initialized = true;
michael@0 251
michael@0 252 this._programActorsCache = [];
michael@0 253 this._contentObserver = new ContentObserver(this.tabActor);
michael@0 254 this._webglObserver = new WebGLObserver();
michael@0 255
michael@0 256 on(this._contentObserver, "global-created", this._onGlobalCreated);
michael@0 257 on(this._contentObserver, "global-destroyed", this._onGlobalDestroyed);
michael@0 258 on(this._webglObserver, "program-linked", this._onProgramLinked);
michael@0 259
michael@0 260 if (reload) {
michael@0 261 this.tabActor.window.location.reload();
michael@0 262 }
michael@0 263 }, {
michael@0 264 request: { reload: Option(0, "boolean") },
michael@0 265 oneway: true
michael@0 266 }),
michael@0 267
michael@0 268 /**
michael@0 269 * Stops listening for document global changes and puts this actor
michael@0 270 * to hibernation. This method is called automatically just before the
michael@0 271 * actor is destroyed.
michael@0 272 */
michael@0 273 finalize: method(function() {
michael@0 274 if (!this._initialized) {
michael@0 275 return;
michael@0 276 }
michael@0 277 this._initialized = false;
michael@0 278
michael@0 279 this._contentObserver.stopListening();
michael@0 280 off(this._contentObserver, "global-created", this._onGlobalCreated);
michael@0 281 off(this._contentObserver, "global-destroyed", this._onGlobalDestroyed);
michael@0 282 off(this._webglObserver, "program-linked", this._onProgramLinked);
michael@0 283
michael@0 284 this._programActorsCache = null;
michael@0 285 this._contentObserver = null;
michael@0 286 this._webglObserver = null;
michael@0 287 }, {
michael@0 288 oneway: true
michael@0 289 }),
michael@0 290
michael@0 291 /**
michael@0 292 * Gets an array of cached program actors for the current tab actor's window.
michael@0 293 * This is useful for dealing with bfcache, when no new programs are linked.
michael@0 294 */
michael@0 295 getPrograms: method(function() {
michael@0 296 let id = ContentObserver.GetInnerWindowID(this.tabActor.window);
michael@0 297 return this._programActorsCache.filter(e => e.ownerWindow == id);
michael@0 298 }, {
michael@0 299 response: { programs: RetVal("array:gl-program") }
michael@0 300 }),
michael@0 301
michael@0 302 /**
michael@0 303 * Events emitted by this actor. The "program-linked" event is fired
michael@0 304 * every time a WebGL program was linked with its respective two shaders.
michael@0 305 */
michael@0 306 events: {
michael@0 307 "program-linked": {
michael@0 308 type: "programLinked",
michael@0 309 program: Arg(0, "gl-program")
michael@0 310 }
michael@0 311 },
michael@0 312
michael@0 313 /**
michael@0 314 * Invoked whenever the current tab actor's document global is created.
michael@0 315 */
michael@0 316 _onGlobalCreated: function(window) {
michael@0 317 WebGLInstrumenter.handle(window, this._webglObserver);
michael@0 318 },
michael@0 319
michael@0 320 /**
michael@0 321 * Invoked whenever the current tab actor's inner window is destroyed.
michael@0 322 */
michael@0 323 _onGlobalDestroyed: function(id) {
michael@0 324 removeFromArray(this._programActorsCache, e => e.ownerWindow == id);
michael@0 325 this._webglObserver.unregisterContextsForWindow(id);
michael@0 326 },
michael@0 327
michael@0 328 /**
michael@0 329 * Invoked whenever an observed WebGL context links a program.
michael@0 330 */
michael@0 331 _onProgramLinked: function(...args) {
michael@0 332 let programActor = new ProgramActor(this.conn, args);
michael@0 333 this._programActorsCache.push(programActor);
michael@0 334 events.emit(this, "program-linked", programActor);
michael@0 335 }
michael@0 336 });
michael@0 337
michael@0 338 /**
michael@0 339 * The corresponding Front object for the WebGLActor.
michael@0 340 */
michael@0 341 let WebGLFront = exports.WebGLFront = protocol.FrontClass(WebGLActor, {
michael@0 342 initialize: function(client, { webglActor }) {
michael@0 343 protocol.Front.prototype.initialize.call(this, client, { actor: webglActor });
michael@0 344 client.addActorPool(this);
michael@0 345 this.manage(this);
michael@0 346 }
michael@0 347 });
michael@0 348
michael@0 349 /**
michael@0 350 * Instruments a HTMLCanvasElement with the appropriate inspection methods.
michael@0 351 */
michael@0 352 let WebGLInstrumenter = {
michael@0 353 /**
michael@0 354 * Overrides the getContext method in the HTMLCanvasElement prototype.
michael@0 355 *
michael@0 356 * @param nsIDOMWindow window
michael@0 357 * The window to perform the instrumentation in.
michael@0 358 * @param WebGLObserver observer
michael@0 359 * The observer watching function calls in the context.
michael@0 360 */
michael@0 361 handle: function(window, observer) {
michael@0 362 let self = this;
michael@0 363
michael@0 364 let id = ContentObserver.GetInnerWindowID(window);
michael@0 365 let canvasElem = XPCNativeWrapper.unwrap(window.HTMLCanvasElement);
michael@0 366 let canvasPrototype = canvasElem.prototype;
michael@0 367 let originalGetContext = canvasPrototype.getContext;
michael@0 368
michael@0 369 /**
michael@0 370 * Returns a drawing context on the canvas, or null if the context ID is
michael@0 371 * not supported. This override creates an observer for the targeted context
michael@0 372 * type and instruments specific functions in the targeted context instance.
michael@0 373 */
michael@0 374 canvasPrototype.getContext = function(name, options) {
michael@0 375 // Make sure a context was able to be created.
michael@0 376 let context = originalGetContext.call(this, name, options);
michael@0 377 if (!context) {
michael@0 378 return context;
michael@0 379 }
michael@0 380 // Make sure a WebGL (not a 2D) context will be instrumented.
michael@0 381 if (WEBGL_CONTEXT_NAMES.indexOf(name) == -1) {
michael@0 382 return context;
michael@0 383 }
michael@0 384 // Repeated calls to 'getContext' return the same instance, no need to
michael@0 385 // instrument everything again.
michael@0 386 if (observer.for(context)) {
michael@0 387 return context;
michael@0 388 }
michael@0 389
michael@0 390 // Create a separate state storage for this context.
michael@0 391 observer.registerContextForWindow(id, context);
michael@0 392
michael@0 393 // Link our observer to the new WebGL context methods.
michael@0 394 for (let { timing, callback, functions } of self._methods) {
michael@0 395 for (let func of functions) {
michael@0 396 self._instrument(observer, context, func, callback, timing);
michael@0 397 }
michael@0 398 }
michael@0 399
michael@0 400 // Return the decorated context back to the content consumer, which
michael@0 401 // will continue using it normally.
michael@0 402 return context;
michael@0 403 };
michael@0 404 },
michael@0 405
michael@0 406 /**
michael@0 407 * Overrides a specific method in a HTMLCanvasElement context.
michael@0 408 *
michael@0 409 * @param WebGLObserver observer
michael@0 410 * The observer watching function calls in the context.
michael@0 411 * @param WebGLRenderingContext context
michael@0 412 * The targeted WebGL context instance.
michael@0 413 * @param string funcName
michael@0 414 * The function to override.
michael@0 415 * @param array callbackName [optional]
michael@0 416 * The two callback function names in the observer, corresponding to
michael@0 417 * the "before" and "after" invocation times. If unspecified, they will
michael@0 418 * default to the name of the function to override.
michael@0 419 * @param number timing [optional]
michael@0 420 * When to issue the callback in relation to the actual context
michael@0 421 * function call. Availalble values are -1 for "before" (default)
michael@0 422 * 1 for "after" and 0 for "before and after".
michael@0 423 */
michael@0 424 _instrument: function(observer, context, funcName, callbackName = [], timing = -1) {
michael@0 425 let { cache, proxy } = observer.for(context);
michael@0 426 let originalFunc = context[funcName];
michael@0 427 let beforeFuncName = callbackName[0] || funcName;
michael@0 428 let afterFuncName = callbackName[1] || callbackName[0] || funcName;
michael@0 429
michael@0 430 context[funcName] = function(...glArgs) {
michael@0 431 if (timing <= 0 && !observer.suppressHandlers) {
michael@0 432 let glBreak = observer[beforeFuncName](glArgs, cache, proxy);
michael@0 433 if (glBreak) return undefined;
michael@0 434 }
michael@0 435
michael@0 436 let glResult = originalFunc.apply(this, glArgs);
michael@0 437
michael@0 438 if (timing >= 0 && !observer.suppressHandlers) {
michael@0 439 let glBreak = observer[afterFuncName](glArgs, glResult, cache, proxy);
michael@0 440 if (glBreak) return undefined;
michael@0 441 }
michael@0 442
michael@0 443 return glResult;
michael@0 444 };
michael@0 445 },
michael@0 446
michael@0 447 /**
michael@0 448 * Override mappings for WebGL methods.
michael@0 449 */
michael@0 450 _methods: [{
michael@0 451 timing: 1, // after
michael@0 452 functions: [
michael@0 453 "linkProgram", "getAttribLocation", "getUniformLocation"
michael@0 454 ]
michael@0 455 }, {
michael@0 456 timing: -1, // before
michael@0 457 callback: [
michael@0 458 "toggleVertexAttribArray"
michael@0 459 ],
michael@0 460 functions: [
michael@0 461 "enableVertexAttribArray", "disableVertexAttribArray"
michael@0 462 ]
michael@0 463 }, {
michael@0 464 timing: -1, // before
michael@0 465 callback: [
michael@0 466 "attribute_"
michael@0 467 ],
michael@0 468 functions: [
michael@0 469 "vertexAttrib1f", "vertexAttrib2f", "vertexAttrib3f", "vertexAttrib4f",
michael@0 470 "vertexAttrib1fv", "vertexAttrib2fv", "vertexAttrib3fv", "vertexAttrib4fv",
michael@0 471 "vertexAttribPointer"
michael@0 472 ]
michael@0 473 }, {
michael@0 474 timing: -1, // before
michael@0 475 callback: [
michael@0 476 "uniform_"
michael@0 477 ],
michael@0 478 functions: [
michael@0 479 "uniform1i", "uniform2i", "uniform3i", "uniform4i",
michael@0 480 "uniform1f", "uniform2f", "uniform3f", "uniform4f",
michael@0 481 "uniform1iv", "uniform2iv", "uniform3iv", "uniform4iv",
michael@0 482 "uniform1fv", "uniform2fv", "uniform3fv", "uniform4fv",
michael@0 483 "uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv"
michael@0 484 ]
michael@0 485 }, {
michael@0 486 timing: -1, // before
michael@0 487 functions: [
michael@0 488 "useProgram", "enable", "disable", "blendColor",
michael@0 489 "blendEquation", "blendEquationSeparate",
michael@0 490 "blendFunc", "blendFuncSeparate"
michael@0 491 ]
michael@0 492 }, {
michael@0 493 timing: 0, // before and after
michael@0 494 callback: [
michael@0 495 "beforeDraw_", "afterDraw_"
michael@0 496 ],
michael@0 497 functions: [
michael@0 498 "drawArrays", "drawElements"
michael@0 499 ]
michael@0 500 }]
michael@0 501 // TODO: It'd be a good idea to handle other functions as well:
michael@0 502 // - getActiveUniform
michael@0 503 // - getUniform
michael@0 504 // - getActiveAttrib
michael@0 505 // - getVertexAttrib
michael@0 506 };
michael@0 507
michael@0 508 /**
michael@0 509 * An observer that captures a WebGL context's method calls.
michael@0 510 */
michael@0 511 function WebGLObserver() {
michael@0 512 this._contexts = new Map();
michael@0 513 }
michael@0 514
michael@0 515 WebGLObserver.prototype = {
michael@0 516 _contexts: null,
michael@0 517
michael@0 518 /**
michael@0 519 * Creates a WebGLCache and a WebGLProxy for the specified window and context.
michael@0 520 *
michael@0 521 * @param number id
michael@0 522 * The id of the window containing the WebGL context.
michael@0 523 * @param WebGLRenderingContext context
michael@0 524 * The WebGL context used in the cache and proxy instances.
michael@0 525 */
michael@0 526 registerContextForWindow: function(id, context) {
michael@0 527 let cache = new WebGLCache(id, context);
michael@0 528 let proxy = new WebGLProxy(id, context, cache, this);
michael@0 529 cache.refreshState(proxy);
michael@0 530
michael@0 531 this._contexts.set(context, {
michael@0 532 ownerWindow: id,
michael@0 533 cache: cache,
michael@0 534 proxy: proxy
michael@0 535 });
michael@0 536 },
michael@0 537
michael@0 538 /**
michael@0 539 * Removes all WebGLCache and WebGLProxy instances for a particular window.
michael@0 540 *
michael@0 541 * @param number id
michael@0 542 * The id of the window containing the WebGL context.
michael@0 543 */
michael@0 544 unregisterContextsForWindow: function(id) {
michael@0 545 removeFromMap(this._contexts, e => e.ownerWindow == id);
michael@0 546 },
michael@0 547
michael@0 548 /**
michael@0 549 * Gets the WebGLCache and WebGLProxy instances for a particular context.
michael@0 550 *
michael@0 551 * @param WebGLRenderingContext context
michael@0 552 * The WebGL context used in the cache and proxy instances.
michael@0 553 * @return object
michael@0 554 * An object containing the corresponding { cache, proxy } instances.
michael@0 555 */
michael@0 556 for: function(context) {
michael@0 557 return this._contexts.get(context);
michael@0 558 },
michael@0 559
michael@0 560 /**
michael@0 561 * Set this flag to true to stop observing any context function calls.
michael@0 562 */
michael@0 563 suppressHandlers: false,
michael@0 564
michael@0 565 /**
michael@0 566 * Called immediately *after* 'linkProgram' is requested in the context.
michael@0 567 *
michael@0 568 * @param array glArgs
michael@0 569 * Overridable arguments with which the function is called.
michael@0 570 * @param void glResult
michael@0 571 * The returned value of the original function call.
michael@0 572 * @param WebGLCache cache
michael@0 573 * The state storage for the WebGL context initiating this call.
michael@0 574 * @param WebGLProxy proxy
michael@0 575 * The proxy methods for the WebGL context initiating this call.
michael@0 576 */
michael@0 577 linkProgram: function(glArgs, glResult, cache, proxy) {
michael@0 578 let program = glArgs[0];
michael@0 579 let shaders = proxy.getAttachedShaders(program);
michael@0 580 cache.addProgram(program, PROGRAM_DEFAULT_TRAITS);
michael@0 581 emit(this, "program-linked", program, shaders, cache, proxy);
michael@0 582 },
michael@0 583
michael@0 584 /**
michael@0 585 * Called immediately *after* 'getAttribLocation' is requested in the context.
michael@0 586 *
michael@0 587 * @param array glArgs
michael@0 588 * Overridable arguments with which the function is called.
michael@0 589 * @param GLint glResult
michael@0 590 * The returned value of the original function call.
michael@0 591 * @param WebGLCache cache
michael@0 592 * The state storage for the WebGL context initiating this call.
michael@0 593 */
michael@0 594 getAttribLocation: function(glArgs, glResult, cache) {
michael@0 595 // Make sure the attribute's value is legal before caching.
michael@0 596 if (glResult < 0) {
michael@0 597 return;
michael@0 598 }
michael@0 599 let [program, name] = glArgs;
michael@0 600 cache.addAttribute(program, name, glResult);
michael@0 601 },
michael@0 602
michael@0 603 /**
michael@0 604 * Called immediately *after* 'getUniformLocation' is requested in the context.
michael@0 605 *
michael@0 606 * @param array glArgs
michael@0 607 * Overridable arguments with which the function is called.
michael@0 608 * @param WebGLUniformLocation glResult
michael@0 609 * The returned value of the original function call.
michael@0 610 * @param WebGLCache cache
michael@0 611 * The state storage for the WebGL context initiating this call.
michael@0 612 */
michael@0 613 getUniformLocation: function(glArgs, glResult, cache) {
michael@0 614 // Make sure the uniform's value is legal before caching.
michael@0 615 if (!glResult) {
michael@0 616 return;
michael@0 617 }
michael@0 618 let [program, name] = glArgs;
michael@0 619 cache.addUniform(program, name, glResult);
michael@0 620 },
michael@0 621
michael@0 622 /**
michael@0 623 * Called immediately *before* 'enableVertexAttribArray' or
michael@0 624 * 'disableVertexAttribArray'is requested in the context.
michael@0 625 *
michael@0 626 * @param array glArgs
michael@0 627 * Overridable arguments with which the function is called.
michael@0 628 * @param WebGLCache cache
michael@0 629 * The state storage for the WebGL context initiating this call.
michael@0 630 */
michael@0 631 toggleVertexAttribArray: function(glArgs, cache) {
michael@0 632 glArgs[0] = cache.getCurrentAttributeLocation(glArgs[0]);
michael@0 633 return glArgs[0] < 0; // Return true to break original function call.
michael@0 634 },
michael@0 635
michael@0 636 /**
michael@0 637 * Called immediately *before* 'attribute_' is requested in the context.
michael@0 638 *
michael@0 639 * @param array glArgs
michael@0 640 * Overridable arguments with which the function is called.
michael@0 641 * @param WebGLCache cache
michael@0 642 * The state storage for the WebGL context initiating this call.
michael@0 643 */
michael@0 644 attribute_: function(glArgs, cache) {
michael@0 645 glArgs[0] = cache.getCurrentAttributeLocation(glArgs[0]);
michael@0 646 return glArgs[0] < 0; // Return true to break original function call.
michael@0 647 },
michael@0 648
michael@0 649 /**
michael@0 650 * Called immediately *before* 'uniform_' is requested in the context.
michael@0 651 *
michael@0 652 * @param array glArgs
michael@0 653 * Overridable arguments with which the function is called.
michael@0 654 * @param WebGLCache cache
michael@0 655 * The state storage for the WebGL context initiating this call.
michael@0 656 */
michael@0 657 uniform_: function(glArgs, cache) {
michael@0 658 glArgs[0] = cache.getCurrentUniformLocation(glArgs[0]);
michael@0 659 return !glArgs[0]; // Return true to break original function call.
michael@0 660 },
michael@0 661
michael@0 662 /**
michael@0 663 * Called immediately *before* 'useProgram' is requested in the context.
michael@0 664 *
michael@0 665 * @param array glArgs
michael@0 666 * Overridable arguments with which the function is called.
michael@0 667 * @param WebGLCache cache
michael@0 668 * The state storage for the WebGL context initiating this call.
michael@0 669 */
michael@0 670 useProgram: function(glArgs, cache) {
michael@0 671 // Manually keeping a cache and not using gl.getParameter(CURRENT_PROGRAM)
michael@0 672 // because gl.get* functions are slow as potatoes.
michael@0 673 cache.currentProgram = glArgs[0];
michael@0 674 },
michael@0 675
michael@0 676 /**
michael@0 677 * Called immediately *before* 'enable' is requested in the context.
michael@0 678 *
michael@0 679 * @param array glArgs
michael@0 680 * Overridable arguments with which the function is called.
michael@0 681 * @param WebGLCache cache
michael@0 682 * The state storage for the WebGL context initiating this call.
michael@0 683 */
michael@0 684 enable: function(glArgs, cache) {
michael@0 685 cache.currentState[glArgs[0]] = true;
michael@0 686 },
michael@0 687
michael@0 688 /**
michael@0 689 * Called immediately *before* 'disable' is requested in the context.
michael@0 690 *
michael@0 691 * @param array glArgs
michael@0 692 * Overridable arguments with which the function is called.
michael@0 693 * @param WebGLCache cache
michael@0 694 * The state storage for the WebGL context initiating this call.
michael@0 695 */
michael@0 696 disable: function(glArgs, cache) {
michael@0 697 cache.currentState[glArgs[0]] = false;
michael@0 698 },
michael@0 699
michael@0 700 /**
michael@0 701 * Called immediately *before* 'blendColor' is requested in the context.
michael@0 702 *
michael@0 703 * @param array glArgs
michael@0 704 * Overridable arguments with which the function is called.
michael@0 705 * @param WebGLCache cache
michael@0 706 * The state storage for the WebGL context initiating this call.
michael@0 707 */
michael@0 708 blendColor: function(glArgs, cache) {
michael@0 709 let blendColor = cache.currentState.blendColor;
michael@0 710 blendColor[0] = glArgs[0];
michael@0 711 blendColor[1] = glArgs[1];
michael@0 712 blendColor[2] = glArgs[2];
michael@0 713 blendColor[3] = glArgs[3];
michael@0 714 },
michael@0 715
michael@0 716 /**
michael@0 717 * Called immediately *before* 'blendEquation' is requested in the context.
michael@0 718 *
michael@0 719 * @param array glArgs
michael@0 720 * Overridable arguments with which the function is called.
michael@0 721 * @param WebGLCache cache
michael@0 722 * The state storage for the WebGL context initiating this call.
michael@0 723 */
michael@0 724 blendEquation: function(glArgs, cache) {
michael@0 725 let state = cache.currentState;
michael@0 726 state.blendEquationRgb = state.blendEquationAlpha = glArgs[0];
michael@0 727 },
michael@0 728
michael@0 729 /**
michael@0 730 * Called immediately *before* 'blendEquationSeparate' is requested in the context.
michael@0 731 *
michael@0 732 * @param array glArgs
michael@0 733 * Overridable arguments with which the function is called.
michael@0 734 * @param WebGLCache cache
michael@0 735 * The state storage for the WebGL context initiating this call.
michael@0 736 */
michael@0 737 blendEquationSeparate: function(glArgs, cache) {
michael@0 738 let state = cache.currentState;
michael@0 739 state.blendEquationRgb = glArgs[0];
michael@0 740 state.blendEquationAlpha = glArgs[1];
michael@0 741 },
michael@0 742
michael@0 743 /**
michael@0 744 * Called immediately *before* 'blendFunc' is requested in the context.
michael@0 745 *
michael@0 746 * @param array glArgs
michael@0 747 * Overridable arguments with which the function is called.
michael@0 748 * @param WebGLCache cache
michael@0 749 * The state storage for the WebGL context initiating this call.
michael@0 750 */
michael@0 751 blendFunc: function(glArgs, cache) {
michael@0 752 let state = cache.currentState;
michael@0 753 state.blendSrcRgb = state.blendSrcAlpha = glArgs[0];
michael@0 754 state.blendDstRgb = state.blendDstAlpha = glArgs[1];
michael@0 755 },
michael@0 756
michael@0 757 /**
michael@0 758 * Called immediately *before* 'blendFuncSeparate' is requested in the context.
michael@0 759 *
michael@0 760 * @param array glArgs
michael@0 761 * Overridable arguments with which the function is called.
michael@0 762 * @param WebGLCache cache
michael@0 763 * The state storage for the WebGL context initiating this call.
michael@0 764 */
michael@0 765 blendFuncSeparate: function(glArgs, cache) {
michael@0 766 let state = cache.currentState;
michael@0 767 state.blendSrcRgb = glArgs[0];
michael@0 768 state.blendDstRgb = glArgs[1];
michael@0 769 state.blendSrcAlpha = glArgs[2];
michael@0 770 state.blendDstAlpha = glArgs[3];
michael@0 771 },
michael@0 772
michael@0 773 /**
michael@0 774 * Called immediately *before* 'drawArrays' or 'drawElements' is requested
michael@0 775 * in the context.
michael@0 776 *
michael@0 777 * @param array glArgs
michael@0 778 * Overridable arguments with which the function is called.
michael@0 779 * @param WebGLCache cache
michael@0 780 * The state storage for the WebGL context initiating this call.
michael@0 781 * @param WebGLProxy proxy
michael@0 782 * The proxy methods for the WebGL context initiating this call.
michael@0 783 */
michael@0 784 beforeDraw_: function(glArgs, cache, proxy) {
michael@0 785 let traits = cache.currentProgramTraits;
michael@0 786
michael@0 787 // Handle program blackboxing.
michael@0 788 if (traits & PROGRAM_BLACKBOX_TRAIT) {
michael@0 789 return true; // Return true to break original function call.
michael@0 790 }
michael@0 791 // Handle program highlighting.
michael@0 792 if (traits & PROGRAM_HIGHLIGHT_TRAIT) {
michael@0 793 proxy.enableHighlighting();
michael@0 794 }
michael@0 795
michael@0 796 return false;
michael@0 797 },
michael@0 798
michael@0 799 /**
michael@0 800 * Called immediately *after* 'drawArrays' or 'drawElements' is requested
michael@0 801 * in the context.
michael@0 802 *
michael@0 803 * @param array glArgs
michael@0 804 * Overridable arguments with which the function is called.
michael@0 805 * @param void glResult
michael@0 806 * The returned value of the original function call.
michael@0 807 * @param WebGLCache cache
michael@0 808 * The state storage for the WebGL context initiating this call.
michael@0 809 * @param WebGLProxy proxy
michael@0 810 * The proxy methods for the WebGL context initiating this call.
michael@0 811 */
michael@0 812 afterDraw_: function(glArgs, glResult, cache, proxy) {
michael@0 813 let traits = cache.currentProgramTraits;
michael@0 814
michael@0 815 // Handle program highlighting.
michael@0 816 if (traits & PROGRAM_HIGHLIGHT_TRAIT) {
michael@0 817 proxy.disableHighlighting();
michael@0 818 }
michael@0 819 }
michael@0 820 };
michael@0 821
michael@0 822 /**
michael@0 823 * A mechanism for storing a single WebGL context's state, programs, shaders,
michael@0 824 * attributes or uniforms.
michael@0 825 *
michael@0 826 * @param number id
michael@0 827 * The id of the window containing the WebGL context.
michael@0 828 * @param WebGLRenderingContext context
michael@0 829 * The WebGL context for which the state is stored.
michael@0 830 */
michael@0 831 function WebGLCache(id, context) {
michael@0 832 this._id = id;
michael@0 833 this._gl = context;
michael@0 834 this._programs = new Map();
michael@0 835 this.currentState = {};
michael@0 836 }
michael@0 837
michael@0 838 WebGLCache.prototype = {
michael@0 839 _id: 0,
michael@0 840 _gl: null,
michael@0 841 _programs: null,
michael@0 842 _currentProgramInfo: null,
michael@0 843 _currentAttributesMap: null,
michael@0 844 _currentUniformsMap: null,
michael@0 845
michael@0 846 get ownerWindow() this._id,
michael@0 847 get ownerContext() this._gl,
michael@0 848
michael@0 849 /**
michael@0 850 * A collection of flags or properties representing the context's state.
michael@0 851 * Implemented as an object hash and not a Map instance because keys are
michael@0 852 * always either strings or numbers.
michael@0 853 */
michael@0 854 currentState: null,
michael@0 855
michael@0 856 /**
michael@0 857 * Populates the current state with values retrieved from the context.
michael@0 858 *
michael@0 859 * @param WebGLProxy proxy
michael@0 860 * The proxy methods for the WebGL context owning the state.
michael@0 861 */
michael@0 862 refreshState: function(proxy) {
michael@0 863 let gl = this._gl;
michael@0 864 let s = this.currentState;
michael@0 865
michael@0 866 // Populate only with the necessary parameters. Not all default WebGL
michael@0 867 // state values are required.
michael@0 868 s[gl.BLEND] = proxy.isEnabled("BLEND");
michael@0 869 s.blendColor = proxy.getParameter("BLEND_COLOR");
michael@0 870 s.blendEquationRgb = proxy.getParameter("BLEND_EQUATION_RGB");
michael@0 871 s.blendEquationAlpha = proxy.getParameter("BLEND_EQUATION_ALPHA");
michael@0 872 s.blendSrcRgb = proxy.getParameter("BLEND_SRC_RGB");
michael@0 873 s.blendSrcAlpha = proxy.getParameter("BLEND_SRC_ALPHA");
michael@0 874 s.blendDstRgb = proxy.getParameter("BLEND_DST_RGB");
michael@0 875 s.blendDstAlpha = proxy.getParameter("BLEND_DST_ALPHA");
michael@0 876 },
michael@0 877
michael@0 878 /**
michael@0 879 * Adds a program to the cache.
michael@0 880 *
michael@0 881 * @param WebGLProgram program
michael@0 882 * The shader for which the traits are to be cached.
michael@0 883 * @param number traits
michael@0 884 * A default properties mask set for the program.
michael@0 885 */
michael@0 886 addProgram: function(program, traits) {
michael@0 887 this._programs.set(program, {
michael@0 888 traits: traits,
michael@0 889 attributes: [], // keys are GLints (numbers)
michael@0 890 uniforms: new Map() // keys are WebGLUniformLocations (objects)
michael@0 891 });
michael@0 892 },
michael@0 893
michael@0 894 /**
michael@0 895 * Adds a specific trait to a program. The effect of such properties is
michael@0 896 * determined by the consumer of this cache.
michael@0 897 *
michael@0 898 * @param WebGLProgram program
michael@0 899 * The program to add the trait to.
michael@0 900 * @param number trait
michael@0 901 * The property added to the program.
michael@0 902 */
michael@0 903 setProgramTrait: function(program, trait) {
michael@0 904 this._programs.get(program).traits |= trait;
michael@0 905 },
michael@0 906
michael@0 907 /**
michael@0 908 * Removes a specific trait from a program.
michael@0 909 *
michael@0 910 * @param WebGLProgram program
michael@0 911 * The program to remove the trait from.
michael@0 912 * @param number trait
michael@0 913 * The property removed from the program.
michael@0 914 */
michael@0 915 unsetProgramTrait: function(program, trait) {
michael@0 916 this._programs.get(program).traits &= ~trait;
michael@0 917 },
michael@0 918
michael@0 919 /**
michael@0 920 * Sets the currently used program in the context.
michael@0 921 * @param WebGLProgram program
michael@0 922 */
michael@0 923 set currentProgram(program) {
michael@0 924 let programInfo = this._programs.get(program);
michael@0 925 if (programInfo == null) {
michael@0 926 return;
michael@0 927 }
michael@0 928 this._currentProgramInfo = programInfo;
michael@0 929 this._currentAttributesMap = programInfo.attributes;
michael@0 930 this._currentUniformsMap = programInfo.uniforms;
michael@0 931 },
michael@0 932
michael@0 933 /**
michael@0 934 * Gets the traits for the currently used program.
michael@0 935 * @return number
michael@0 936 */
michael@0 937 get currentProgramTraits() {
michael@0 938 return this._currentProgramInfo.traits;
michael@0 939 },
michael@0 940
michael@0 941 /**
michael@0 942 * Adds an attribute to the cache.
michael@0 943 *
michael@0 944 * @param WebGLProgram program
michael@0 945 * The program for which the attribute is bound.
michael@0 946 * @param string name
michael@0 947 * The attribute name.
michael@0 948 * @param GLint value
michael@0 949 * The attribute value.
michael@0 950 */
michael@0 951 addAttribute: function(program, name, value) {
michael@0 952 this._programs.get(program).attributes[value] = {
michael@0 953 name: name,
michael@0 954 value: value
michael@0 955 };
michael@0 956 },
michael@0 957
michael@0 958 /**
michael@0 959 * Adds a uniform to the cache.
michael@0 960 *
michael@0 961 * @param WebGLProgram program
michael@0 962 * The program for which the uniform is bound.
michael@0 963 * @param string name
michael@0 964 * The uniform name.
michael@0 965 * @param WebGLUniformLocation value
michael@0 966 * The uniform value.
michael@0 967 */
michael@0 968 addUniform: function(program, name, value) {
michael@0 969 this._programs.get(program).uniforms.set(new XPCNativeWrapper(value), {
michael@0 970 name: name,
michael@0 971 value: value
michael@0 972 });
michael@0 973 },
michael@0 974
michael@0 975 /**
michael@0 976 * Updates the attribute locations for a specific program.
michael@0 977 * This is necessary, for example, when the shader is relinked and all the
michael@0 978 * attribute locations become obsolete.
michael@0 979 *
michael@0 980 * @param WebGLProgram program
michael@0 981 * The program for which the attributes need updating.
michael@0 982 */
michael@0 983 updateAttributesForProgram: function(program) {
michael@0 984 let attributes = this._programs.get(program).attributes;
michael@0 985 for (let attribute of attributes) {
michael@0 986 attribute.value = this._gl.getAttribLocation(program, attribute.name);
michael@0 987 }
michael@0 988 },
michael@0 989
michael@0 990 /**
michael@0 991 * Updates the uniform locations for a specific program.
michael@0 992 * This is necessary, for example, when the shader is relinked and all the
michael@0 993 * uniform locations become obsolete.
michael@0 994 *
michael@0 995 * @param WebGLProgram program
michael@0 996 * The program for which the uniforms need updating.
michael@0 997 */
michael@0 998 updateUniformsForProgram: function(program) {
michael@0 999 let uniforms = this._programs.get(program).uniforms;
michael@0 1000 for (let [, uniform] of uniforms) {
michael@0 1001 uniform.value = this._gl.getUniformLocation(program, uniform.name);
michael@0 1002 }
michael@0 1003 },
michael@0 1004
michael@0 1005 /**
michael@0 1006 * Gets the actual attribute location in a specific program.
michael@0 1007 * When relinked, all the attribute locations become obsolete and are updated
michael@0 1008 * in the cache. This method returns the (current) real attribute location.
michael@0 1009 *
michael@0 1010 * @param GLint initialValue
michael@0 1011 * The initial attribute value.
michael@0 1012 * @return GLint
michael@0 1013 * The current attribute value, or the initial value if it's already
michael@0 1014 * up to date with its corresponding program.
michael@0 1015 */
michael@0 1016 getCurrentAttributeLocation: function(initialValue) {
michael@0 1017 let attributes = this._currentAttributesMap;
michael@0 1018 let currentInfo = attributes ? attributes[initialValue] : null;
michael@0 1019 return currentInfo ? currentInfo.value : initialValue;
michael@0 1020 },
michael@0 1021
michael@0 1022 /**
michael@0 1023 * Gets the actual uniform location in a specific program.
michael@0 1024 * When relinked, all the uniform locations become obsolete and are updated
michael@0 1025 * in the cache. This method returns the (current) real uniform location.
michael@0 1026 *
michael@0 1027 * @param WebGLUniformLocation initialValue
michael@0 1028 * The initial uniform value.
michael@0 1029 * @return WebGLUniformLocation
michael@0 1030 * The current uniform value, or the initial value if it's already
michael@0 1031 * up to date with its corresponding program.
michael@0 1032 */
michael@0 1033 getCurrentUniformLocation: function(initialValue) {
michael@0 1034 let uniforms = this._currentUniformsMap;
michael@0 1035 let currentInfo = uniforms ? uniforms.get(initialValue) : null;
michael@0 1036 return currentInfo ? currentInfo.value : initialValue;
michael@0 1037 }
michael@0 1038 };
michael@0 1039
michael@0 1040 /**
michael@0 1041 * A mechanism for injecting or qureying state into/from a single WebGL context.
michael@0 1042 *
michael@0 1043 * Any interaction with a WebGL context should go through this proxy.
michael@0 1044 * Otherwise, the corresponding observer would register the calls as coming
michael@0 1045 * from content, which is usually not desirable. Infinite call stacks are bad.
michael@0 1046 *
michael@0 1047 * @param number id
michael@0 1048 * The id of the window containing the WebGL context.
michael@0 1049 * @param WebGLRenderingContext context
michael@0 1050 * The WebGL context used for the proxy methods.
michael@0 1051 * @param WebGLCache cache
michael@0 1052 * The state storage for the corresponding context.
michael@0 1053 * @param WebGLObserver observer
michael@0 1054 * The observer watching function calls in the corresponding context.
michael@0 1055 */
michael@0 1056 function WebGLProxy(id, context, cache, observer) {
michael@0 1057 this._id = id;
michael@0 1058 this._gl = context;
michael@0 1059 this._cache = cache;
michael@0 1060 this._observer = observer;
michael@0 1061
michael@0 1062 let exports = [
michael@0 1063 "isEnabled",
michael@0 1064 "getParameter",
michael@0 1065 "getAttachedShaders",
michael@0 1066 "getShaderSource",
michael@0 1067 "getShaderOfType",
michael@0 1068 "compileShader",
michael@0 1069 "enableHighlighting",
michael@0 1070 "disableHighlighting"
michael@0 1071 ];
michael@0 1072 exports.forEach(e => this[e] = (...args) => this._call(e, args));
michael@0 1073 }
michael@0 1074
michael@0 1075 WebGLProxy.prototype = {
michael@0 1076 _id: 0,
michael@0 1077 _gl: null,
michael@0 1078 _cache: null,
michael@0 1079 _observer: null,
michael@0 1080
michael@0 1081 get ownerWindow() this._id,
michael@0 1082 get ownerContext() this._gl,
michael@0 1083
michael@0 1084 /**
michael@0 1085 * Test whether a WebGL capability is enabled.
michael@0 1086 *
michael@0 1087 * @param string name
michael@0 1088 * The WebGL capability name, for example "BLEND".
michael@0 1089 * @return boolean
michael@0 1090 * True if enabled, false otherwise.
michael@0 1091 */
michael@0 1092 _isEnabled: function(name) {
michael@0 1093 return this._gl.isEnabled(this._gl[name]);
michael@0 1094 },
michael@0 1095
michael@0 1096 /**
michael@0 1097 * Returns the value for the specified WebGL parameter name.
michael@0 1098 *
michael@0 1099 * @param string name
michael@0 1100 * The WebGL parameter name, for example "BLEND_COLOR".
michael@0 1101 * @return any
michael@0 1102 * The corresponding parameter's value.
michael@0 1103 */
michael@0 1104 _getParameter: function(name) {
michael@0 1105 return this._gl.getParameter(this._gl[name]);
michael@0 1106 },
michael@0 1107
michael@0 1108 /**
michael@0 1109 * Returns the renderbuffer property value for the specified WebGL parameter.
michael@0 1110 * If no renderbuffer binding is available, null is returned.
michael@0 1111 *
michael@0 1112 * @param string name
michael@0 1113 * The WebGL parameter name, for example "BLEND_COLOR".
michael@0 1114 * @return any
michael@0 1115 * The corresponding parameter's value.
michael@0 1116 */
michael@0 1117 _getRenderbufferParameter: function(name) {
michael@0 1118 if (!this._getParameter("RENDERBUFFER_BINDING")) {
michael@0 1119 return null;
michael@0 1120 }
michael@0 1121 let gl = this._gl;
michael@0 1122 return gl.getRenderbufferParameter(gl.RENDERBUFFER, gl[name]);
michael@0 1123 },
michael@0 1124
michael@0 1125 /**
michael@0 1126 * Returns the framebuffer property value for the specified WebGL parameter.
michael@0 1127 * If no framebuffer binding is available, null is returned.
michael@0 1128 *
michael@0 1129 * @param string type
michael@0 1130 * The framebuffer object attachment point, for example "COLOR_ATTACHMENT0".
michael@0 1131 * @param string name
michael@0 1132 * The WebGL parameter name, for example "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME".
michael@0 1133 * If unspecified, defaults to "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE".
michael@0 1134 * @return any
michael@0 1135 * The corresponding parameter's value.
michael@0 1136 */
michael@0 1137 _getFramebufferAttachmentParameter: function(type, name = "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE") {
michael@0 1138 if (!this._getParameter("FRAMEBUFFER_BINDING")) {
michael@0 1139 return null;
michael@0 1140 }
michael@0 1141 let gl = this._gl;
michael@0 1142 return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl[type], gl[name]);
michael@0 1143 },
michael@0 1144
michael@0 1145 /**
michael@0 1146 * Returns the shader objects attached to a program object.
michael@0 1147 *
michael@0 1148 * @param WebGLProgram program
michael@0 1149 * The program for which to retrieve the attached shaders.
michael@0 1150 * @return array
michael@0 1151 * The attached vertex and fragment shaders.
michael@0 1152 */
michael@0 1153 _getAttachedShaders: function(program) {
michael@0 1154 return this._gl.getAttachedShaders(program);
michael@0 1155 },
michael@0 1156
michael@0 1157 /**
michael@0 1158 * Returns the source code string from a shader object.
michael@0 1159 *
michael@0 1160 * @param WebGLShader shader
michael@0 1161 * The shader for which to retrieve the source code.
michael@0 1162 * @return string
michael@0 1163 * The shader's source code.
michael@0 1164 */
michael@0 1165 _getShaderSource: function(shader) {
michael@0 1166 return this._gl.getShaderSource(shader);
michael@0 1167 },
michael@0 1168
michael@0 1169 /**
michael@0 1170 * Finds a shader of the specified type in a list.
michael@0 1171 *
michael@0 1172 * @param WebGLShader[] shaders
michael@0 1173 * The shaders for which to check the type.
michael@0 1174 * @param string type
michael@0 1175 * Either "vertex" or "fragment".
michael@0 1176 * @return WebGLShader | null
michael@0 1177 * The shader of the specified type, or null if nothing is found.
michael@0 1178 */
michael@0 1179 _getShaderOfType: function(shaders, type) {
michael@0 1180 let gl = this._gl;
michael@0 1181 let shaderTypeEnum = {
michael@0 1182 vertex: gl.VERTEX_SHADER,
michael@0 1183 fragment: gl.FRAGMENT_SHADER
michael@0 1184 }[type];
michael@0 1185
michael@0 1186 for (let shader of shaders) {
michael@0 1187 if (gl.getShaderParameter(shader, gl.SHADER_TYPE) == shaderTypeEnum) {
michael@0 1188 return shader;
michael@0 1189 }
michael@0 1190 }
michael@0 1191 return null;
michael@0 1192 },
michael@0 1193
michael@0 1194 /**
michael@0 1195 * Changes a shader's source code and relinks the respective program.
michael@0 1196 *
michael@0 1197 * @param WebGLProgram program
michael@0 1198 * The program who's linked shader is to be modified.
michael@0 1199 * @param WebGLShader shader
michael@0 1200 * The shader to be modified.
michael@0 1201 * @param string text
michael@0 1202 * The new shader source code.
michael@0 1203 * @return object
michael@0 1204 * An object containing the compilation and linking status.
michael@0 1205 */
michael@0 1206 _compileShader: function(program, shader, text) {
michael@0 1207 let gl = this._gl;
michael@0 1208 gl.shaderSource(shader, text);
michael@0 1209 gl.compileShader(shader);
michael@0 1210 gl.linkProgram(program);
michael@0 1211
michael@0 1212 let error = { compile: "", link: "" };
michael@0 1213
michael@0 1214 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
michael@0 1215 error.compile = gl.getShaderInfoLog(shader);
michael@0 1216 }
michael@0 1217 if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
michael@0 1218 error.link = gl.getShaderInfoLog(shader);
michael@0 1219 }
michael@0 1220
michael@0 1221 this._cache.updateAttributesForProgram(program);
michael@0 1222 this._cache.updateUniformsForProgram(program);
michael@0 1223
michael@0 1224 return error;
michael@0 1225 },
michael@0 1226
michael@0 1227 /**
michael@0 1228 * Enables color blending based on the geometry highlight tint.
michael@0 1229 */
michael@0 1230 _enableHighlighting: function() {
michael@0 1231 let gl = this._gl;
michael@0 1232
michael@0 1233 // Avoid changing the blending params when "rendering to texture".
michael@0 1234
michael@0 1235 // Check drawing to a custom framebuffer bound to the default renderbuffer.
michael@0 1236 let hasFramebuffer = this._getParameter("FRAMEBUFFER_BINDING");
michael@0 1237 let hasRenderbuffer = this._getParameter("RENDERBUFFER_BINDING");
michael@0 1238 if (hasFramebuffer && !hasRenderbuffer) {
michael@0 1239 return;
michael@0 1240 }
michael@0 1241
michael@0 1242 // Check drawing to a depth or stencil component of the framebuffer.
michael@0 1243 let writesDepth = this._getFramebufferAttachmentParameter("DEPTH_ATTACHMENT");
michael@0 1244 let writesStencil = this._getFramebufferAttachmentParameter("STENCIL_ATTACHMENT");
michael@0 1245 if (writesDepth || writesStencil) {
michael@0 1246 return;
michael@0 1247 }
michael@0 1248
michael@0 1249 // Non-premultiplied alpha blending based on a predefined constant color.
michael@0 1250 // Simply using gl.colorMask won't work, because we want non-tinted colors
michael@0 1251 // to be drawn as black, not ignored.
michael@0 1252 gl.enable(gl.BLEND);
michael@0 1253 gl.blendColor.apply(gl, this.highlightTint);
michael@0 1254 gl.blendEquation(gl.FUNC_ADD);
michael@0 1255 gl.blendFunc(gl.CONSTANT_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.CONSTANT_COLOR, gl.ZERO);
michael@0 1256 this.wasHighlighting = true;
michael@0 1257 },
michael@0 1258
michael@0 1259 /**
michael@0 1260 * Disables color blending based on the geometry highlight tint, by
michael@0 1261 * reverting the corresponding params back to their original values.
michael@0 1262 */
michael@0 1263 _disableHighlighting: function() {
michael@0 1264 let gl = this._gl;
michael@0 1265 let s = this._cache.currentState;
michael@0 1266
michael@0 1267 gl[s[gl.BLEND] ? "enable" : "disable"](gl.BLEND);
michael@0 1268 gl.blendColor.apply(gl, s.blendColor);
michael@0 1269 gl.blendEquationSeparate(s.blendEquationRgb, s.blendEquationAlpha);
michael@0 1270 gl.blendFuncSeparate(s.blendSrcRgb, s.blendDstRgb, s.blendSrcAlpha, s.blendDstAlpha);
michael@0 1271 },
michael@0 1272
michael@0 1273 /**
michael@0 1274 * The color tint used for highlighting geometry.
michael@0 1275 * @see _enableHighlighting and _disableHighlighting.
michael@0 1276 */
michael@0 1277 highlightTint: [0, 0, 0, 0],
michael@0 1278
michael@0 1279 /**
michael@0 1280 * Executes a function in this object.
michael@0 1281 *
michael@0 1282 * This method makes sure that any handlers in the context observer are
michael@0 1283 * suppressed, hence stopping observing any context function calls.
michael@0 1284 *
michael@0 1285 * @param string funcName
michael@0 1286 * The function to call.
michael@0 1287 * @param array args
michael@0 1288 * An array of arguments.
michael@0 1289 * @return any
michael@0 1290 * The called function result.
michael@0 1291 */
michael@0 1292 _call: function(funcName, args) {
michael@0 1293 let prevState = this._observer.suppressHandlers;
michael@0 1294
michael@0 1295 this._observer.suppressHandlers = true;
michael@0 1296 let result = this["_" + funcName].apply(this, args);
michael@0 1297 this._observer.suppressHandlers = prevState;
michael@0 1298
michael@0 1299 return result;
michael@0 1300 }
michael@0 1301 };
michael@0 1302
michael@0 1303 // Utility functions.
michael@0 1304
michael@0 1305 function removeFromMap(map, predicate) {
michael@0 1306 for (let [key, value] of map) {
michael@0 1307 if (predicate(value)) {
michael@0 1308 map.delete(key);
michael@0 1309 }
michael@0 1310 }
michael@0 1311 };
michael@0 1312
michael@0 1313 function removeFromArray(array, predicate) {
michael@0 1314 for (let value of array) {
michael@0 1315 if (predicate(value)) {
michael@0 1316 array.splice(array.indexOf(value), 1);
michael@0 1317 }
michael@0 1318 }
michael@0 1319 }

mercurial