1 /* 2 * Copyright (C) 2014 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @constructor 33 * @extends {WebInspector.VBox} 34 */ 35 WebInspector.Layers3DView = function() 36 { 37 WebInspector.VBox.call(this); 38 this.element.classList.add("layers-3d-view"); 39 this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("Layer information is not yet available.")); 40 41 this._transformController = new WebInspector.TransformController(this.element); 42 this._transformController.addEventListener(WebInspector.TransformController.Events.TransformChanged, this._update, this); 43 this._initStatusBar(); 44 45 this._canvasElement = this.element.createChild("canvas"); 46 this._canvasElement.tabIndex = 0; 47 this._canvasElement.addEventListener("dblclick", this._onDoubleClick.bind(this), false); 48 this._canvasElement.addEventListener("mousedown", this._onMouseDown.bind(this), false); 49 this._canvasElement.addEventListener("mouseup", this._onMouseUp.bind(this), false); 50 this._canvasElement.addEventListener("mouseout", this._onMouseMove.bind(this), false); 51 this._canvasElement.addEventListener("mousemove", this._onMouseMove.bind(this), false); 52 this._canvasElement.addEventListener("contextmenu", this._onContextMenu.bind(this), false); 53 54 this._lastActiveObject = {}; 55 this._picturesForLayer = {}; 56 this._scrollRectQuadsForLayer = {}; 57 this._isVisible = {}; 58 this._layerTree = null; 59 this._textureManager = new WebInspector.LayerTextureManager(); 60 this._textureManager.addEventListener(WebInspector.LayerTextureManager.Events.TextureUpdated, this._update, this); 61 62 WebInspector.settings.showPaintRects.addChangeListener(this._update, this); 63 } 64 65 /** @typedef {{borderColor: !Array.<number>, borderWidth: number}} */ 66 WebInspector.Layers3DView.LayerStyle; 67 68 /** @typedef {{layerId: string, rect: !Array.<number>, snapshot: !WebInspector.PaintProfilerSnapshot, traceEvent: !WebInspector.TracingModel.Event}} */ 69 WebInspector.Layers3DView.PaintTile; 70 71 /** 72 * @enum {string} 73 */ 74 WebInspector.Layers3DView.OutlineType = { 75 Hovered: "hovered", 76 Selected: "selected" 77 } 78 79 /** 80 * @enum {string} 81 */ 82 WebInspector.Layers3DView.Events = { 83 ObjectHovered: "ObjectHovered", 84 ObjectSelected: "ObjectSelected", 85 LayerSnapshotRequested: "LayerSnapshotRequested", 86 JumpToPaintEventRequested: "JumpToPaintEventRequested" 87 } 88 89 /** 90 * @enum {string} 91 */ 92 WebInspector.Layers3DView.ScrollRectTitles = { 93 RepaintsOnScroll: WebInspector.UIString("repaints on scroll"), 94 TouchEventHandler: WebInspector.UIString("touch event listener"), 95 WheelEventHandler: WebInspector.UIString("mousewheel event listener") 96 } 97 98 WebInspector.Layers3DView.FragmentShader = "\ 99 precision mediump float;\ 100 varying vec4 vColor;\ 101 varying vec2 vTextureCoord;\ 102 uniform sampler2D uSampler;\ 103 void main(void)\ 104 {\ 105 gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)) * vColor;\ 106 }"; 107 108 WebInspector.Layers3DView.VertexShader = "\ 109 attribute vec3 aVertexPosition;\ 110 attribute vec2 aTextureCoord;\ 111 attribute vec4 aVertexColor;\ 112 uniform mat4 uPMatrix;\ 113 varying vec2 vTextureCoord;\ 114 varying vec4 vColor;\ 115 void main(void)\ 116 {\ 117 gl_Position = uPMatrix * vec4(aVertexPosition, 1.0);\ 118 vColor = aVertexColor;\ 119 vTextureCoord = aTextureCoord;\ 120 }"; 121 122 WebInspector.Layers3DView.SelectedBackgroundColor = [20, 40, 110, 0.66]; 123 WebInspector.Layers3DView.BackgroundColor = [0, 0, 0, 0]; 124 WebInspector.Layers3DView.HoveredBorderColor = [0, 0, 255, 1]; 125 WebInspector.Layers3DView.SelectedBorderColor = [0, 255, 0, 1]; 126 WebInspector.Layers3DView.BorderColor = [0, 0, 0, 1]; 127 WebInspector.Layers3DView.ScrollRectBackgroundColor = [178, 0, 0, 0.4]; 128 WebInspector.Layers3DView.SelectedScrollRectBackgroundColor = [178, 0, 0, 0.6]; 129 WebInspector.Layers3DView.ScrollRectBorderColor = [178, 0, 0, 1]; 130 WebInspector.Layers3DView.BorderWidth = 1; 131 WebInspector.Layers3DView.SelectedBorderWidth = 2; 132 133 WebInspector.Layers3DView.LayerSpacing = 20; 134 WebInspector.Layers3DView.ScrollRectSpacing = 4; 135 136 WebInspector.Layers3DView.prototype = { 137 /** 138 * @param {?WebInspector.LayerTreeBase} layerTree 139 */ 140 setLayerTree: function(layerTree) 141 { 142 this._layerTree = layerTree; 143 this._textureManager.reset(); 144 this._update(); 145 }, 146 147 /** 148 * @param {?Array.<!WebInspector.Layers3DView.PaintTile>} tiles 149 */ 150 setTiles: function(tiles) 151 { 152 this._textureManager.setTiles(tiles); 153 }, 154 155 /** 156 * @param {!WebInspector.Layer} layer 157 * @param {string=} imageURL 158 */ 159 showImageForLayer: function(layer, imageURL) 160 { 161 this._textureManager.createTexture(onTextureCreated.bind(this), imageURL); 162 163 /** 164 * @this {WebInspector.Layers3DView} 165 * @param {!WebGLTexture} texture 166 */ 167 function onTextureCreated(texture) 168 { 169 this._layerTexture = {layerId: layer.id(), texture: texture}; 170 this._update(); 171 } 172 }, 173 174 onResize: function() 175 { 176 this._update(); 177 }, 178 179 wasShown: function() 180 { 181 if (this._needsUpdate) 182 this._update(); 183 }, 184 185 /** 186 * @param {!WebInspector.Layers3DView.OutlineType} type 187 * @param {?WebInspector.Layers3DView.ActiveObject} activeObject 188 */ 189 _setOutline: function(type, activeObject) 190 { 191 this._lastActiveObject[type] = activeObject; 192 this._update(); 193 }, 194 195 /** 196 * @param {?WebInspector.Layers3DView.ActiveObject} activeObject 197 */ 198 hoverObject: function(activeObject) 199 { 200 this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, activeObject); 201 }, 202 203 /** 204 * @param {?WebInspector.Layers3DView.ActiveObject} activeObject 205 */ 206 selectObject: function(activeObject) 207 { 208 this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, null); 209 this._setOutline(WebInspector.Layers3DView.OutlineType.Selected, activeObject); 210 }, 211 212 /** 213 * @param {!Element} canvas 214 * @return {!WebGLRenderingContext} 215 */ 216 _initGL: function(canvas) 217 { 218 var gl = canvas.getContext("webgl"); 219 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 220 gl.enable(gl.BLEND); 221 gl.clearColor(0.0, 0.0, 0.0, 0.0); 222 gl.enable(gl.DEPTH_TEST); 223 return gl; 224 }, 225 226 /** 227 * @param {!Object} type 228 * @param {string} script 229 */ 230 _createShader: function(type, script) 231 { 232 var shader = this._gl.createShader(type); 233 this._gl.shaderSource(shader, script); 234 this._gl.compileShader(shader); 235 this._gl.attachShader(this._shaderProgram, shader); 236 }, 237 238 _initShaders: function() 239 { 240 this._shaderProgram = this._gl.createProgram(); 241 this._createShader(this._gl.FRAGMENT_SHADER, WebInspector.Layers3DView.FragmentShader); 242 this._createShader(this._gl.VERTEX_SHADER, WebInspector.Layers3DView.VertexShader); 243 this._gl.linkProgram(this._shaderProgram); 244 this._gl.useProgram(this._shaderProgram); 245 246 this._shaderProgram.vertexPositionAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexPosition"); 247 this._gl.enableVertexAttribArray(this._shaderProgram.vertexPositionAttribute); 248 this._shaderProgram.vertexColorAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexColor"); 249 this._gl.enableVertexAttribArray(this._shaderProgram.vertexColorAttribute); 250 this._shaderProgram.textureCoordAttribute = this._gl.getAttribLocation(this._shaderProgram, "aTextureCoord"); 251 this._gl.enableVertexAttribArray(this._shaderProgram.textureCoordAttribute); 252 253 this._shaderProgram.pMatrixUniform = this._gl.getUniformLocation(this._shaderProgram, "uPMatrix"); 254 this._shaderProgram.samplerUniform = this._gl.getUniformLocation(this._shaderProgram, "uSampler"); 255 }, 256 257 _resizeCanvas: function() 258 { 259 this._canvasElement.width = this._canvasElement.offsetWidth * window.devicePixelRatio; 260 this._canvasElement.height = this._canvasElement.offsetHeight * window.devicePixelRatio; 261 this._gl.viewportWidth = this._canvasElement.width; 262 this._gl.viewportHeight = this._canvasElement.height; 263 }, 264 265 /** 266 * @return {!CSSMatrix} 267 */ 268 _calculateProjectionMatrix: function() 269 { 270 var scaleFactorForMargins = 1.2; 271 var viewport = this._layerTree.viewportSize(); 272 var baseWidth = viewport ? viewport.width : this._layerTree.contentRoot().width(); 273 var baseHeight = viewport ? viewport.height : this._layerTree.contentRoot().height(); 274 var canvasWidth = this._canvasElement.width; 275 var canvasHeight = this._canvasElement.height; 276 var scaleX = canvasWidth / baseWidth / scaleFactorForMargins; 277 var scaleY = canvasHeight / baseHeight / scaleFactorForMargins; 278 var viewScale = Math.min(scaleX, scaleY); 279 var scale = this._transformController.scale(); 280 var offsetX = this._transformController.offsetX() * window.devicePixelRatio; 281 var offsetY = this._transformController.offsetY() * window.devicePixelRatio; 282 var rotateX = this._transformController.rotateX(); 283 var rotateY = this._transformController.rotateY(); 284 return new WebKitCSSMatrix().translate(offsetX, offsetY, 0).scale(scale, scale, scale).translate(canvasWidth / 2, canvasHeight / 2, 0) 285 .rotate(rotateX, rotateY, 0).scale(viewScale, viewScale, viewScale).translate(-baseWidth / 2, -baseHeight / 2, 0); 286 }, 287 288 /** 289 * @param {!CSSMatrix} m 290 * @return {!Float32Array} 291 */ 292 _arrayFromMatrix: function(m) 293 { 294 return new Float32Array([m.m11, m.m12, m.m13, m.m14, m.m21, m.m22, m.m23, m.m24, m.m31, m.m32, m.m33, m.m34, m.m41, m.m42, m.m43, m.m44]); 295 }, 296 297 _initProjectionMatrix: function() 298 { 299 var projectionMatrix = this._calculateProjectionMatrix(); 300 this._pMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0) 301 .scale(2 / this._canvasElement.width, 2 / this._canvasElement.height, 1 / 1000000).multiply(projectionMatrix); 302 this._gl.uniformMatrix4fv(this._shaderProgram.pMatrixUniform, false, this._arrayFromMatrix(this._pMatrix)); 303 this._textureScale = Math.min(1, Math.max(projectionMatrix.m11, projectionMatrix.m22)); 304 }, 305 306 _initWhiteTexture: function() 307 { 308 this._whiteTexture = this._gl.createTexture(); 309 this._gl.bindTexture(this._gl.TEXTURE_2D, this._whiteTexture); 310 var whitePixel = new Uint8Array([255, 255, 255, 255]); 311 this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, 1, 1, 0, this._gl.RGBA, this._gl.UNSIGNED_BYTE, whitePixel); 312 }, 313 314 _initGLIfNecessary: function() 315 { 316 if (this._gl) 317 return this._gl; 318 this._gl = this._initGL(this._canvasElement); 319 this._initShaders(); 320 this._initWhiteTexture(); 321 this._textureManager.setContext(this._gl); 322 return this._gl; 323 }, 324 325 _calculateDepths: function() 326 { 327 this._depthByLayerId = {}; 328 this._isVisible = {}; 329 var depth = 0; 330 var root = this._layerTree.root(); 331 var queue = [root]; 332 this._depthByLayerId[root.id()] = 0; 333 this._isVisible[root.id()] = false; 334 while (queue.length > 0) { 335 var layer = queue.shift(); 336 var children = layer.children(); 337 for (var i = 0; i < children.length; ++i) { 338 this._depthByLayerId[children[i].id()] = ++depth; 339 this._isVisible[children[i].id()] = children[i] === this._layerTree.contentRoot() || this._isVisible[layer.id()]; 340 queue.push(children[i]); 341 } 342 } 343 this._maxDepth = depth; 344 }, 345 346 /** 347 * @param {!WebInspector.Layers3DView.OutlineType} type 348 * @param {!WebInspector.Layer} layer 349 * @param {number=} scrollRectIndex 350 */ 351 _isObjectActive: function(type, layer, scrollRectIndex) 352 { 353 var activeObject = this._lastActiveObject[type]; 354 return activeObject && activeObject.layer && activeObject.layer.id() === layer.id() && (typeof scrollRectIndex !== "number" || activeObject.scrollRectIndex === scrollRectIndex); 355 }, 356 357 /** 358 * @param {!WebInspector.Layer} layer 359 * @return {!WebInspector.Layers3DView.LayerStyle} 360 */ 361 _styleForLayer: function(layer) 362 { 363 var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer); 364 var isHovered = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Hovered, layer); 365 var borderColor; 366 if (isSelected) 367 borderColor = WebInspector.Layers3DView.SelectedBorderColor; 368 else if (isHovered) 369 borderColor = WebInspector.Layers3DView.HoveredBorderColor; 370 else 371 borderColor = WebInspector.Layers3DView.BorderColor; 372 var borderWidth = isSelected ? WebInspector.Layers3DView.SelectedBorderWidth : WebInspector.Layers3DView.BorderWidth; 373 return {borderColor: borderColor, borderWidth: borderWidth}; 374 }, 375 376 /** 377 * @param {!WebInspector.Layer} layer 378 * @return {number} 379 */ 380 _depthForLayer: function(layer) 381 { 382 return this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing; 383 }, 384 385 /** 386 * @param {!WebInspector.Layer} layer 387 * @param {number} index 388 * @return {number} 389 */ 390 _calculateScrollRectDepth: function(layer, index) 391 { 392 return this._depthForLayer(layer) + index * WebInspector.Layers3DView.ScrollRectSpacing + 1; 393 }, 394 395 /** 396 * @param {!WebInspector.Layer} layer 397 */ 398 _calculateLayerRect: function(layer) 399 { 400 if (!this._isVisible[layer.id()]) 401 return; 402 var activeObject = WebInspector.Layers3DView.ActiveObject.createLayerActiveObject(layer); 403 var rect = new WebInspector.Layers3DView.Rectangle(activeObject); 404 var style = this._styleForLayer(layer); 405 rect.setVertices(layer.quad(), this._depthForLayer(layer)); 406 rect.lineWidth = style.borderWidth; 407 rect.borderColor = style.borderColor; 408 this._rects.push(rect); 409 }, 410 411 /** 412 * @param {!WebInspector.Layer} layer 413 */ 414 _calculateLayerScrollRects: function(layer) 415 { 416 var scrollRects = layer.scrollRects(); 417 for (var i = 0; i < scrollRects.length; ++i) { 418 var activeObject = WebInspector.Layers3DView.ActiveObject.createScrollRectActiveObject(layer, i); 419 var rect = new WebInspector.Layers3DView.Rectangle(activeObject); 420 rect.calculateVerticesFromRect(layer, scrollRects[i].rect, this._calculateScrollRectDepth(layer, i)); 421 var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer, i); 422 var color = isSelected ? WebInspector.Layers3DView.SelectedScrollRectBackgroundColor : WebInspector.Layers3DView.ScrollRectBackgroundColor; 423 rect.fillColor = color; 424 rect.borderColor = WebInspector.Layers3DView.ScrollRectBorderColor; 425 this._rects.push(rect); 426 } 427 }, 428 429 /** 430 * @param {!WebInspector.Layer} layer 431 */ 432 _calculateLayerImageRect: function(layer) 433 { 434 var layerTexture = this._layerTexture; 435 if (layer.id() !== layerTexture.layerId) 436 return; 437 var activeObject = WebInspector.Layers3DView.ActiveObject.createLayerActiveObject(layer); 438 var rect = new WebInspector.Layers3DView.Rectangle(activeObject); 439 rect.setVertices(layer.quad(), this._depthForLayer(layer)); 440 rect.texture = layerTexture.texture; 441 this._rects.push(rect); 442 }, 443 444 /** 445 * @param {!WebInspector.Layer} layer 446 */ 447 _calculateLayerTileRects: function(layer) 448 { 449 var tiles = this._textureManager.tilesForLayer(layer.id()); 450 for (var i = 0; i < tiles.length; ++i) { 451 var tile = tiles[i]; 452 if (!tile.texture) 453 continue; 454 var activeObject = WebInspector.Layers3DView.ActiveObject.createTileActiveObject(layer, tile.traceEvent); 455 var rect = new WebInspector.Layers3DView.Rectangle(activeObject); 456 rect.calculateVerticesFromRect(layer, {x: tile.rect[0], y: tile.rect[1], width: tile.rect[2], height: tile.rect[3]}, this._depthForLayer(layer) + 1); 457 rect.texture = tile.texture; 458 this._rects.push(rect); 459 } 460 }, 461 462 _calculateViewportRect: function() 463 { 464 var rect = new WebInspector.Layers3DView.Rectangle(null); 465 var viewport = this._layerTree.viewportSize(); 466 var depth = (this._maxDepth + 1) * WebInspector.Layers3DView.LayerSpacing; 467 var vertices = [0, 0, depth, viewport.width, 0, depth, viewport.width, viewport.height, depth, 0, viewport.height, depth]; 468 rect.vertices = vertices; 469 rect.borderColor = [0, 0, 0, 1]; 470 rect.lineWidth = 3; 471 this._rects.push(rect); 472 }, 473 474 _calculateRects: function() 475 { 476 this._rects = []; 477 478 this._layerTree.forEachLayer(this._calculateLayerRect.bind(this)); 479 480 if (this._showSlowScrollRectsSetting.get()) 481 this._layerTree.forEachLayer(this._calculateLayerScrollRects.bind(this)); 482 483 if (this._showPaintsSetting.get()) { 484 if (this._layerTexture) 485 this._layerTree.forEachLayer(this._calculateLayerImageRect.bind(this)); 486 else 487 this._layerTree.forEachLayer(this._calculateLayerTileRects.bind(this)); 488 } 489 490 if (this._layerTree.viewportSize() && this._showViewportSetting.get()) 491 this._calculateViewportRect(); 492 }, 493 494 /** 495 * @param {!Array.<number>} color 496 * @return {!Array.<number>} 497 */ 498 _makeColorsArray: function(color) 499 { 500 var colors = []; 501 var normalizedColor = [color[0] / 255, color[1] / 255, color[2] / 255, color[3]]; 502 for (var i = 0; i < 4; i++) { 503 colors = colors.concat(normalizedColor); 504 } 505 return colors; 506 }, 507 508 /** 509 * @param {!Object} attribute 510 * @param {!Array.<number>} array 511 * @param {!number} length 512 */ 513 _setVertexAttribute: function(attribute, array, length) 514 { 515 var gl = this._gl; 516 var buffer = gl.createBuffer(); 517 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 518 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), gl.STATIC_DRAW); 519 gl.vertexAttribPointer(attribute, length, gl.FLOAT, false, 0, 0); 520 }, 521 522 /** 523 * @param {!Array.<number>} vertices 524 * @param {boolean} isBorder 525 * @param {!Array.<number>=} color 526 * @param {!Object=} texture 527 */ 528 _drawRectangle: function(vertices, isBorder, color, texture) 529 { 530 var gl = this._gl; 531 var glMode = isBorder ? gl.LINE_LOOP : gl.TRIANGLE_FAN; 532 var white = [255, 255, 255, 1]; 533 this._setVertexAttribute(this._shaderProgram.vertexPositionAttribute, vertices, 3); 534 this._setVertexAttribute(this._shaderProgram.textureCoordAttribute, [0, 1, 1, 1, 1, 0, 0, 0], 2); 535 536 if (texture) { 537 this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(white), white.length); 538 gl.activeTexture(gl.TEXTURE0); 539 gl.bindTexture(gl.TEXTURE_2D, texture); 540 gl.uniform1i(this._shaderProgram.samplerUniform, 0); 541 } else { 542 this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(color || white), color.length); 543 gl.bindTexture(gl.TEXTURE_2D, this._whiteTexture); 544 } 545 546 var numberOfVertices = 4; 547 gl.drawArrays(glMode, 0, numberOfVertices); 548 }, 549 550 /** 551 * @param {!WebInspector.Layers3DView.Rectangle} rect 552 */ 553 _drawViewRect: function(rect) 554 { 555 var vertices = rect.vertices; 556 if (rect.texture) 557 this._drawRectangle(vertices, false, undefined, rect.texture); 558 else if (rect.fillColor) 559 this._drawRectangle(vertices, false, rect.fillColor); 560 this._gl.lineWidth(rect.lineWidth); 561 if (rect.borderColor) 562 this._drawRectangle(vertices, true, rect.borderColor); 563 }, 564 565 _update: function() 566 { 567 if (!this.isShowing()) { 568 this._needsUpdate = true; 569 return; 570 } 571 var contentRoot = this._layerTree && this._layerTree.contentRoot(); 572 if (!contentRoot || !this._layerTree.root()) { 573 this._emptyView.show(this.element); 574 return; 575 } 576 this._emptyView.detach(); 577 578 var gl = this._initGLIfNecessary(); 579 this._resizeCanvas(); 580 this._initProjectionMatrix(); 581 this._calculateDepths(); 582 583 this._textureManager.setScale(this._textureScale); 584 gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); 585 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 586 587 this._calculateRects(); 588 this._rects.forEach(this._drawViewRect.bind(this)); 589 }, 590 591 /** 592 * @param {!Event} event 593 * @return {?WebInspector.Layers3DView.ActiveObject} 594 */ 595 _activeObjectFromEventPoint: function(event) 596 { 597 if (!this._layerTree) 598 return null; 599 var closestIntersectionPoint = Infinity; 600 var closestObject = null; 601 var projectionMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0).multiply(this._calculateProjectionMatrix()); 602 var x0 = (event.clientX - this._canvasElement.totalOffsetLeft()) * window.devicePixelRatio; 603 var y0 = -(event.clientY - this._canvasElement.totalOffsetTop()) * window.devicePixelRatio; 604 605 /** 606 * @param {!WebInspector.Layers3DView.Rectangle} rect 607 */ 608 function checkIntersection(rect) 609 { 610 if (!rect.relatedObject) 611 return; 612 var t = rect.intersectWithLine(projectionMatrix, x0, y0); 613 if (t < closestIntersectionPoint) { 614 closestIntersectionPoint = t; 615 closestObject = rect.relatedObject; 616 } 617 } 618 619 this._rects.forEach(checkIntersection); 620 return closestObject; 621 }, 622 623 /** 624 * @param {string} caption 625 * @param {string} name 626 * @param {boolean} value 627 * @param {!Element} statusBarElement 628 * @return {!WebInspector.Setting} 629 */ 630 _createVisibilitySetting: function(caption, name, value, statusBarElement) 631 { 632 var checkbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString(caption)) 633 statusBarElement.appendChild(checkbox.element); 634 var setting = WebInspector.settings.createSetting(name, value) 635 WebInspector.SettingsUI.bindCheckbox(checkbox.inputElement, setting); 636 setting.addChangeListener(this._update, this); 637 return setting; 638 }, 639 640 _initStatusBar: function() 641 { 642 this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar"); 643 this._panelStatusBarElement.appendChild(this._transformController.controlPanelElement()); 644 this._showViewportSetting = this._createVisibilitySetting("Viewport", "showViewport", true, this._panelStatusBarElement); 645 this._showSlowScrollRectsSetting = this._createVisibilitySetting("Slow scroll rects", "showSlowScrollRects", true, this._panelStatusBarElement); 646 this._showPaintsSetting = this._createVisibilitySetting("Paints", "showPaints", true, this._panelStatusBarElement); 647 }, 648 649 /** 650 * @param {!Event} event 651 */ 652 _onContextMenu: function(event) 653 { 654 var activeObject = this._activeObjectFromEventPoint(event); 655 var node = activeObject && activeObject.layer && activeObject.layer.nodeForSelfOrAncestor(); 656 var contextMenu = new WebInspector.ContextMenu(event); 657 contextMenu.appendItem(WebInspector.UIString("Reset View"), this._transformController.resetAndNotify.bind(this._transformController), false); 658 if (activeObject && activeObject.type() === WebInspector.Layers3DView.ActiveObject.Type.Tile) 659 contextMenu.appendItem(WebInspector.UIString("Jump to Paint Event"), this.dispatchEventToListeners.bind(this, WebInspector.Layers3DView.Events.JumpToPaintEventRequested, activeObject.traceEvent), false); 660 if (node) 661 contextMenu.appendApplicableItems(node); 662 contextMenu.show(); 663 }, 664 665 /** 666 * @param {!Event} event 667 */ 668 _onMouseMove: function(event) 669 { 670 if (event.which) 671 return; 672 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectHovered, this._activeObjectFromEventPoint(event)); 673 }, 674 675 /** 676 * @param {!Event} event 677 */ 678 _onMouseDown: function(event) 679 { 680 this._mouseDownX = event.clientX; 681 this._mouseDownY = event.clientY; 682 }, 683 684 /** 685 * @param {!Event} event 686 */ 687 _onMouseUp: function(event) 688 { 689 const maxDistanceInPixels = 6; 690 if (this._mouseDownX && Math.abs(event.clientX - this._mouseDownX) < maxDistanceInPixels && Math.abs(event.clientY - this._mouseDownY) < maxDistanceInPixels) 691 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectSelected, this._activeObjectFromEventPoint(event)); 692 delete this._mouseDownX; 693 delete this._mouseDownY; 694 }, 695 696 /** 697 * @param {!Event} event 698 */ 699 _onDoubleClick: function(event) 700 { 701 var object = this._activeObjectFromEventPoint(event); 702 if (object) { 703 if (object.type() == WebInspector.Layers3DView.ActiveObject.Type.Tile) 704 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.JumpToPaintEventRequested, object.traceEvent); 705 else if (object.layer) 706 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.LayerSnapshotRequested, object.layer); 707 } 708 event.stopPropagation(); 709 }, 710 711 __proto__: WebInspector.VBox.prototype 712 } 713 714 /** 715 * @constructor 716 * @extends {WebInspector.Object} 717 */ 718 WebInspector.LayerTextureManager = function() 719 { 720 WebInspector.Object.call(this); 721 this.reset(); 722 } 723 724 WebInspector.LayerTextureManager.Events = { 725 TextureUpdated: "TextureUpated" 726 } 727 728 WebInspector.LayerTextureManager.prototype = { 729 reset: function() 730 { 731 /** @type {!Object.<string, !Array.<!WebInspector.LayerTextureManager.Tile>>} */ 732 this._tilesByLayerId = {}; 733 this._scale = 0; 734 }, 735 736 /** 737 * @param {!WebGLRenderingContext} glContext 738 */ 739 setContext: function(glContext) 740 { 741 this._gl = glContext; 742 if (this._scale) 743 this._updateTextures(); 744 }, 745 746 /** 747 * @param {?Array.<!WebInspector.Layers3DView.PaintTile>} paintTiles 748 */ 749 setTiles: function(paintTiles) 750 { 751 this._tilesByLayerId = {}; 752 if (!paintTiles) 753 return; 754 for (var i = 0; i < paintTiles.length; ++i) { 755 var layerId = paintTiles[i].layerId; 756 var tilesForLayer = this._tilesByLayerId[layerId]; 757 if (!tilesForLayer) { 758 tilesForLayer = []; 759 this._tilesByLayerId[layerId] = tilesForLayer; 760 } 761 var tile = new WebInspector.LayerTextureManager.Tile(paintTiles[i].snapshot, paintTiles[i].rect, paintTiles[i].traceEvent); 762 tilesForLayer.push(tile); 763 if (this._scale && this._gl) 764 this._updateTile(tile); 765 } 766 }, 767 768 /** 769 * @param {number} scale 770 */ 771 setScale: function(scale) 772 { 773 if (this._scale && this._scale >= scale) 774 return; 775 this._scale = scale; 776 this._updateTextures(); 777 }, 778 779 /** 780 * @param {string} layerId 781 * @return {!Array.<!WebInspector.LayerTextureManager.Tile>} 782 */ 783 tilesForLayer: function(layerId) 784 { 785 return this._tilesByLayerId[layerId] || []; 786 }, 787 788 _updateTextures: function() 789 { 790 if (!this._gl) 791 return; 792 if (!this._scale) 793 return; 794 795 for (var layerId in this._tilesByLayerId) { 796 for (var i = 0; i < this._tilesByLayerId[layerId].length; ++i) { 797 var tile = this._tilesByLayerId[layerId][i]; 798 if (!tile.scale || tile.scale < this._scale) 799 this._updateTile(tile); 800 } 801 } 802 }, 803 804 /** 805 * @param {!WebInspector.LayerTextureManager.Tile} tile 806 */ 807 _updateTile: function(tile) 808 { 809 console.assert(this._scale && this._gl); 810 tile.scale = this._scale; 811 tile.snapshot.requestImage(null, null, tile.scale, onGotImage.bind(this)); 812 813 /** 814 * @this {WebInspector.LayerTextureManager} 815 * @param {string=} imageURL 816 */ 817 function onGotImage(imageURL) 818 { 819 this.createTexture(onTextureCreated.bind(this), imageURL); 820 } 821 822 /** 823 * @this {WebInspector.LayerTextureManager} 824 * @param {!WebGLTexture} texture 825 */ 826 function onTextureCreated(texture) 827 { 828 tile.texture = texture; 829 this.dispatchEventToListeners(WebInspector.LayerTextureManager.Events.TextureUpdated); 830 } 831 }, 832 833 /** 834 * @param {!function(!WebGLTexture)} textureCreatedCallback 835 * @param {string=} imageURL 836 */ 837 createTexture: function(textureCreatedCallback, imageURL) 838 { 839 var image = new Image(); 840 image.addEventListener("load", onImageLoaded.bind(this), false); 841 image.src = imageURL; 842 843 /** 844 * @this {WebInspector.LayerTextureManager} 845 */ 846 function onImageLoaded() 847 { 848 textureCreatedCallback(this._createTextureForImage(image)); 849 } 850 }, 851 852 /** 853 * @param {!Image} image 854 * @return {!WebGLTexture} texture 855 */ 856 _createTextureForImage: function(image) 857 { 858 var texture = this._gl.createTexture(); 859 texture.image = image; 860 this._gl.bindTexture(this._gl.TEXTURE_2D, texture); 861 this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, true); 862 this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, this._gl.RGBA, this._gl.UNSIGNED_BYTE, texture.image); 863 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, this._gl.LINEAR); 864 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, this._gl.LINEAR); 865 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, this._gl.CLAMP_TO_EDGE); 866 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, this._gl.CLAMP_TO_EDGE); 867 this._gl.bindTexture(this._gl.TEXTURE_2D, null); 868 return texture; 869 }, 870 871 __proto__: WebInspector.Object.prototype 872 } 873 874 /** 875 * @constructor 876 * @param {?WebInspector.Layers3DView.ActiveObject} relatedObject 877 */ 878 WebInspector.Layers3DView.Rectangle = function(relatedObject) 879 { 880 this.relatedObject = relatedObject; 881 /** @type {number} */ 882 this.lineWidth = 1; 883 /** @type {?Array.<number>} */ 884 this.borderColor = null; 885 /** @type {?Array.<number>} */ 886 this.fillColor = null; 887 /** @type {?WebGLTexture} */ 888 this.texture = null; 889 } 890 891 WebInspector.Layers3DView.Rectangle.prototype = { 892 /** 893 * @param {!Array.<number>} quad 894 * @param {number} z 895 */ 896 setVertices: function(quad, z) 897 { 898 this.vertices = [quad[0], quad[1], z, quad[2], quad[3], z, quad[4], quad[5], z, quad[6], quad[7], z]; 899 }, 900 901 /** 902 * Finds coordinates of point on layer quad, having offsets (ratioX * width) and (ratioY * height) 903 * from the left corner of the initial layer rect, where width and heigth are layer bounds. 904 * @param {!Array.<number>} quad 905 * @param {number} ratioX 906 * @param {number} ratioY 907 * @return {!Array.<number>} 908 */ 909 _calculatePointOnQuad: function(quad, ratioX, ratioY) 910 { 911 var x0 = quad[0]; 912 var y0 = quad[1]; 913 var x1 = quad[2]; 914 var y1 = quad[3]; 915 var x2 = quad[4]; 916 var y2 = quad[5]; 917 var x3 = quad[6]; 918 var y3 = quad[7]; 919 // Point on the first quad side clockwise 920 var firstSidePointX = x0 + ratioX * (x1 - x0); 921 var firstSidePointY = y0 + ratioX * (y1 - y0); 922 // Point on the third quad side clockwise 923 var thirdSidePointX = x3 + ratioX * (x2 - x3); 924 var thirdSidePointY = y3 + ratioX * (y2 - y3); 925 var x = firstSidePointX + ratioY * (thirdSidePointX - firstSidePointX); 926 var y = firstSidePointY + ratioY * (thirdSidePointY - firstSidePointY); 927 return [x, y]; 928 }, 929 930 /** 931 * @param {!WebInspector.Layer} layer 932 * @param {!DOMAgent.Rect} rect 933 * @param {number} z 934 */ 935 calculateVerticesFromRect: function(layer, rect, z) 936 { 937 var quad = layer.quad(); 938 var rx1 = rect.x / layer.width(); 939 var rx2 = (rect.x + rect.width) / layer.width(); 940 var ry1 = rect.y / layer.height(); 941 var ry2 = (rect.y + rect.height) / layer.height(); 942 var rectQuad = this._calculatePointOnQuad(quad, rx1, ry1).concat(this._calculatePointOnQuad(quad, rx2, ry1)) 943 .concat(this._calculatePointOnQuad(quad, rx2, ry2)).concat(this._calculatePointOnQuad(quad, rx1, ry2)); 944 this.setVertices(rectQuad, z); 945 }, 946 947 /** 948 * Intersects quad with given transform matrix and line l(t) = (x0, y0, t) 949 * @param {!CSSMatrix} matrix 950 * @param {!number} x0 951 * @param {!number} y0 952 * @return {(number|undefined)} 953 */ 954 intersectWithLine: function(matrix, x0, y0) 955 { 956 var epsilon = 1e-8; 957 var i; 958 // Vertices of the quad with transform matrix applied 959 var points = []; 960 for (i = 0; i < 4; ++i) 961 points[i] = WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(new WebInspector.Geometry.Vector(this.vertices[i * 3], this.vertices[i * 3 + 1], this.vertices[i * 3 + 2]), matrix); 962 // Calculating quad plane normal 963 var normal = WebInspector.Geometry.crossProduct(WebInspector.Geometry.subtract(points[1], points[0]), WebInspector.Geometry.subtract(points[2], points[1])); 964 // General form of the equation of the quad plane: A * x + B * y + C * z + D = 0 965 var A = normal.x; 966 var B = normal.y; 967 var C = normal.z; 968 var D = -(A * points[0].x + B * points[0].y + C * points[0].z); 969 // Finding t from the equation 970 var t = -(D + A * x0 + B * y0) / C; 971 // Point of the intersection 972 var pt = new WebInspector.Geometry.Vector(x0, y0, t); 973 // Vectors from the intersection point to vertices of the quad 974 var tVects = points.map(WebInspector.Geometry.subtract.bind(null, pt)); 975 // Intersection point lies inside of the polygon if scalar products of normal of the plane and 976 // cross products of successive tVects are all nonstrictly above or all nonstrictly below zero 977 for (i = 0; i < tVects.length; ++i) { 978 var product = WebInspector.Geometry.scalarProduct(normal, WebInspector.Geometry.crossProduct(tVects[i], tVects[(i + 1) % tVects.length])); 979 if (product < 0) 980 return undefined; 981 } 982 return t; 983 } 984 } 985 986 /** 987 * @constructor 988 */ 989 WebInspector.Layers3DView.ActiveObject = function() 990 { 991 } 992 993 /** 994 * @enum {string} 995 */ 996 WebInspector.Layers3DView.ActiveObject.Type = { 997 Layer: "Layer", 998 ScrollRect: "ScrollRect", 999 Tile: "Tile", 1000 }; 1001 1002 /** 1003 * @param {!WebInspector.Layer} layer 1004 * @return {!WebInspector.Layers3DView.ActiveObject} 1005 */ 1006 WebInspector.Layers3DView.ActiveObject.createLayerActiveObject = function(layer) 1007 { 1008 var activeObject = new WebInspector.Layers3DView.ActiveObject(); 1009 activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.Layer; 1010 activeObject.layer = layer; 1011 return activeObject; 1012 } 1013 1014 /** 1015 * @param {!WebInspector.Layer} layer 1016 * @param {number} scrollRectIndex 1017 * @return {!WebInspector.Layers3DView.ActiveObject} 1018 */ 1019 WebInspector.Layers3DView.ActiveObject.createScrollRectActiveObject = function(layer, scrollRectIndex) 1020 { 1021 var activeObject = new WebInspector.Layers3DView.ActiveObject(); 1022 activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.ScrollRect; 1023 activeObject.layer = layer; 1024 activeObject.scrollRectIndex = scrollRectIndex; 1025 return activeObject; 1026 } 1027 1028 /** 1029 * @param {!WebInspector.Layer} layer 1030 * @param {!WebInspector.TracingModel.Event} traceEvent 1031 * @return {!WebInspector.Layers3DView.ActiveObject} 1032 */ 1033 WebInspector.Layers3DView.ActiveObject.createTileActiveObject = function(layer, traceEvent) 1034 { 1035 var activeObject = new WebInspector.Layers3DView.ActiveObject(); 1036 activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.Tile; 1037 activeObject.layer = layer; 1038 activeObject.traceEvent = traceEvent; 1039 return activeObject; 1040 } 1041 1042 WebInspector.Layers3DView.ActiveObject.prototype = { 1043 /** 1044 * @return {!WebInspector.Layers3DView.ActiveObject.Type} 1045 */ 1046 type: function() 1047 { 1048 return this._type; 1049 } 1050 }; 1051 1052 /** 1053 * @constructor 1054 * @param {!WebInspector.PaintProfilerSnapshot} snapshot 1055 * @param {!Array.<number>} rect 1056 * @param {!WebInspector.TracingModel.Event} traceEvent 1057 */ 1058 WebInspector.LayerTextureManager.Tile = function(snapshot, rect, traceEvent) 1059 { 1060 this.snapshot = snapshot; 1061 this.rect = rect; 1062 this.traceEvent = traceEvent; 1063 this.scale = 0; 1064 /** @type {?WebGLTexture} */ 1065 this.texture = null; 1066 } 1067