2 * Copyright (C) 2014 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @extends {WebInspector.VBox}
35 WebInspector.Layers3DView = function()
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."));
41 this._transformController = new WebInspector.TransformController(this.element);
42 this._transformController.addEventListener(WebInspector.TransformController.Events.TransformChanged, this._update, this);
43 this._initStatusBar();
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("mouseleave", 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);
54 this._lastActiveObject = {};
55 this._picturesForLayer = {};
56 this._scrollRectQuadsForLayer = {};
58 this._layerTree = null;
59 this._textureManager = new WebInspector.LayerTextureManager();
60 this._textureManager.addEventListener(WebInspector.LayerTextureManager.Events.TextureUpdated, this._update, this);
61 /** @type Array.<!WebGLTexture|undefined> */
62 this._chromeTextures = [];
64 WebInspector.settings.showPaintRects.addChangeListener(this._update, this);
67 /** @typedef {{borderColor: !Array.<number>, borderWidth: number}} */
68 WebInspector.Layers3DView.LayerStyle;
70 /** @typedef {{layerId: string, rect: !Array.<number>, snapshot: !WebInspector.PaintProfilerSnapshot, traceEvent: !WebInspector.TracingModel.Event}} */
71 WebInspector.Layers3DView.PaintTile;
76 WebInspector.Layers3DView.OutlineType = {
84 WebInspector.Layers3DView.Events = {
85 ObjectHovered: "ObjectHovered",
86 ObjectSelected: "ObjectSelected",
87 LayerSnapshotRequested: "LayerSnapshotRequested",
88 PaintProfilerRequested: "PaintProfilerRequested"
94 WebInspector.Layers3DView.ChromeTexture = {
103 WebInspector.Layers3DView.ScrollRectTitles = {
104 RepaintsOnScroll: WebInspector.UIString("repaints on scroll"),
105 TouchEventHandler: WebInspector.UIString("touch event listener"),
106 WheelEventHandler: WebInspector.UIString("mousewheel event listener")
109 WebInspector.Layers3DView.FragmentShader = "" +
110 "precision mediump float;\n" +
111 "varying vec4 vColor;\n" +
112 "varying vec2 vTextureCoord;\n" +
113 "uniform sampler2D uSampler;\n" +
114 "void main(void)\n" +
116 " gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)) * vColor;\n" +
119 WebInspector.Layers3DView.VertexShader = "" +
120 "attribute vec3 aVertexPosition;\n" +
121 "attribute vec2 aTextureCoord;\n" +
122 "attribute vec4 aVertexColor;\n" +
123 "uniform mat4 uPMatrix;\n" +
124 "varying vec2 vTextureCoord;\n" +
125 "varying vec4 vColor;\n" +
126 "void main(void)\n" +
128 "gl_Position = uPMatrix * vec4(aVertexPosition, 1.0);\n" +
129 "vColor = aVertexColor;\n" +
130 "vTextureCoord = aTextureCoord;\n" +
133 WebInspector.Layers3DView.SelectedBackgroundColor = [20, 40, 110, 0.66];
134 WebInspector.Layers3DView.BackgroundColor = [0, 0, 0, 0];
135 WebInspector.Layers3DView.HoveredBorderColor = [0, 0, 255, 1];
136 WebInspector.Layers3DView.SelectedBorderColor = [0, 255, 0, 1];
137 WebInspector.Layers3DView.BorderColor = [0, 0, 0, 1];
138 WebInspector.Layers3DView.ViewportBorderColor = [160, 160, 160, 1];
139 WebInspector.Layers3DView.ScrollRectBackgroundColor = [178, 0, 0, 0.4];
140 WebInspector.Layers3DView.SelectedScrollRectBackgroundColor = [178, 0, 0, 0.6];
141 WebInspector.Layers3DView.ScrollRectBorderColor = [178, 0, 0, 1];
142 WebInspector.Layers3DView.BorderWidth = 1;
143 WebInspector.Layers3DView.SelectedBorderWidth = 2;
144 WebInspector.Layers3DView.ViewportBorderWidth = 3;
146 WebInspector.Layers3DView.LayerSpacing = 20;
147 WebInspector.Layers3DView.ScrollRectSpacing = 4;
149 WebInspector.Layers3DView.prototype = {
151 * @param {?WebInspector.LayerTreeBase} layerTree
153 setLayerTree: function(layerTree)
155 this._layerTree = layerTree;
156 this._textureManager.reset();
161 * @param {?Array.<!WebInspector.Layers3DView.PaintTile>} tiles
163 setTiles: function(tiles)
165 this._textureManager.setTiles(tiles);
169 * @param {!WebInspector.Layer} layer
170 * @param {string=} imageURL
172 showImageForLayer: function(layer, imageURL)
175 this._textureManager.createTexture(onTextureCreated.bind(this), imageURL);
177 onTextureCreated.call(this, null);
180 * @this {WebInspector.Layers3DView}
181 * @param {?WebGLTexture} texture
183 function onTextureCreated(texture)
185 this._layerTexture = texture ? {layerId: layer.id(), texture: texture} : null;
197 if (this._needsUpdate)
202 * @param {!WebInspector.Layers3DView.OutlineType} type
203 * @param {?WebInspector.Layers3DView.ActiveObject} activeObject
205 _setOutline: function(type, activeObject)
207 this._lastActiveObject[type] = activeObject;
212 * @param {?WebInspector.Layers3DView.ActiveObject} activeObject
214 hoverObject: function(activeObject)
216 this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, activeObject);
220 * @param {?WebInspector.Layers3DView.ActiveObject} activeObject
222 selectObject: function(activeObject)
224 this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, null);
225 this._setOutline(WebInspector.Layers3DView.OutlineType.Selected, activeObject);
229 * @param {!Element} canvas
230 * @return {!WebGLRenderingContext}
232 _initGL: function(canvas)
234 var gl = canvas.getContext("webgl");
235 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
237 gl.clearColor(0.0, 0.0, 0.0, 0.0);
238 gl.enable(gl.DEPTH_TEST);
243 * @param {!Object} type
244 * @param {string} script
246 _createShader: function(type, script)
248 var shader = this._gl.createShader(type);
249 this._gl.shaderSource(shader, script);
250 this._gl.compileShader(shader);
251 this._gl.attachShader(this._shaderProgram, shader);
254 _initShaders: function()
256 this._shaderProgram = this._gl.createProgram();
257 this._createShader(this._gl.FRAGMENT_SHADER, WebInspector.Layers3DView.FragmentShader);
258 this._createShader(this._gl.VERTEX_SHADER, WebInspector.Layers3DView.VertexShader);
259 this._gl.linkProgram(this._shaderProgram);
260 this._gl.useProgram(this._shaderProgram);
262 this._shaderProgram.vertexPositionAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexPosition");
263 this._gl.enableVertexAttribArray(this._shaderProgram.vertexPositionAttribute);
264 this._shaderProgram.vertexColorAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexColor");
265 this._gl.enableVertexAttribArray(this._shaderProgram.vertexColorAttribute);
266 this._shaderProgram.textureCoordAttribute = this._gl.getAttribLocation(this._shaderProgram, "aTextureCoord");
267 this._gl.enableVertexAttribArray(this._shaderProgram.textureCoordAttribute);
269 this._shaderProgram.pMatrixUniform = this._gl.getUniformLocation(this._shaderProgram, "uPMatrix");
270 this._shaderProgram.samplerUniform = this._gl.getUniformLocation(this._shaderProgram, "uSampler");
273 _resizeCanvas: function()
275 this._canvasElement.width = this._canvasElement.offsetWidth * window.devicePixelRatio;
276 this._canvasElement.height = this._canvasElement.offsetHeight * window.devicePixelRatio;
277 this._gl.viewportWidth = this._canvasElement.width;
278 this._gl.viewportHeight = this._canvasElement.height;
281 _updateTransformAndConstraints: function()
283 var paddingFraction = 0.1;
284 var viewport = this._layerTree.viewportSize();
285 var root = this._layerTree.contentRoot() || this._layerTree.root();
286 var baseWidth = viewport ? viewport.width : root.width();
287 var baseHeight = viewport ? viewport.height : root.height();
288 var canvasWidth = this._canvasElement.width;
289 var canvasHeight = this._canvasElement.height;
290 var paddingX = canvasWidth * paddingFraction;
291 var paddingY = canvasHeight * paddingFraction;
292 var scaleX = (canvasWidth - 2 * paddingX) / baseWidth;
293 var scaleY = (canvasHeight - 2 * paddingY) / baseHeight;
294 var viewScale = Math.min(scaleX, scaleY);
295 var minScaleConstraint = Math.min(baseWidth / root.width(), baseHeight / root.height()) / 2;
296 this._transformController.setScaleConstraints(minScaleConstraint, 10 / viewScale); // 1/viewScale is 1:1 in terms of pixels, so allow zooming to 10x of native size
297 var scale = this._transformController.scale();
298 var rotateX = this._transformController.rotateX();
299 var rotateY = this._transformController.rotateY();
300 this._scale = scale * viewScale;
301 var scaleAndRotationMatrix = new WebKitCSSMatrix().scale(scale, scale, scale).translate(canvasWidth / 2, canvasHeight / 2, 0)
302 .rotate(rotateX, rotateY, 0).scale(viewScale, viewScale, viewScale).translate(-baseWidth / 2, -baseHeight / 2, 0);
305 for (var i = 0; i < this._rects.length; ++i)
306 bounds = WebInspector.Geometry.boundsForTransformedPoints(scaleAndRotationMatrix, this._rects[i].vertices, bounds);
308 this._transformController.clampOffsets((paddingX - bounds.maxX) / window.devicePixelRatio, (canvasWidth - paddingX - bounds.minX) / window.devicePixelRatio,
309 (paddingY - bounds.maxY) / window.devicePixelRatio, (canvasHeight - paddingY - bounds.minY) / window.devicePixelRatio);
310 var offsetX = this._transformController.offsetX() * window.devicePixelRatio;
311 var offsetY = this._transformController.offsetY() * window.devicePixelRatio;
312 // Multiply to translation matrix on the right rather than translate (which would implicitly multiply on the left).
313 this._projectionMatrix = new WebKitCSSMatrix().translate(offsetX, offsetY, 0).multiply(scaleAndRotationMatrix);
315 var glProjectionMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0)
316 .scale(2 / this._canvasElement.width, 2 / this._canvasElement.height, 1 / 1000000).multiply(this._projectionMatrix);
317 this._gl.uniformMatrix4fv(this._shaderProgram.pMatrixUniform, false, this._arrayFromMatrix(glProjectionMatrix));
321 * @param {!CSSMatrix} m
322 * @return {!Float32Array}
324 _arrayFromMatrix: function(m)
326 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]);
329 _initWhiteTexture: function()
331 this._whiteTexture = this._gl.createTexture();
332 this._gl.bindTexture(this._gl.TEXTURE_2D, this._whiteTexture);
333 var whitePixel = new Uint8Array([255, 255, 255, 255]);
334 this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, 1, 1, 0, this._gl.RGBA, this._gl.UNSIGNED_BYTE, whitePixel);
337 _initChromeTextures: function()
340 * @this {WebInspector.Layers3DView}
341 * @param {!WebInspector.Layers3DView.ChromeTexture} index
342 * @param {?WebGLTexture} value
344 function saveChromeTexture(index, value)
346 this._chromeTextures[index] = value || undefined;
348 this._textureManager.createTexture(saveChromeTexture.bind(this, WebInspector.Layers3DView.ChromeTexture.Left), "Images/chromeLeft.png");
349 this._textureManager.createTexture(saveChromeTexture.bind(this, WebInspector.Layers3DView.ChromeTexture.Middle), "Images/chromeMiddle.png");
350 this._textureManager.createTexture(saveChromeTexture.bind(this, WebInspector.Layers3DView.ChromeTexture.Right), "Images/chromeRight.png");
353 _initGLIfNecessary: function()
357 this._gl = this._initGL(this._canvasElement);
359 this._initWhiteTexture();
360 this._initChromeTextures();
361 this._textureManager.setContext(this._gl);
365 _calculateDepths: function()
367 this._depthByLayerId = {};
368 this._isVisible = {};
370 var root = this._layerTree.root();
372 this._depthByLayerId[root.id()] = 0;
373 this._isVisible[root.id()] = this._layerTree.root() === root;
374 while (queue.length > 0) {
375 var layer = queue.shift();
376 var children = layer.children();
377 for (var i = 0; i < children.length; ++i) {
378 this._depthByLayerId[children[i].id()] = ++depth;
379 this._isVisible[children[i].id()] = children[i] === this._layerTree.contentRoot() || this._isVisible[layer.id()];
380 queue.push(children[i]);
383 this._maxDepth = depth;
387 * @param {!WebInspector.Layers3DView.OutlineType} type
388 * @param {!WebInspector.Layer} layer
389 * @param {number=} scrollRectIndex
391 _isObjectActive: function(type, layer, scrollRectIndex)
393 var activeObject = this._lastActiveObject[type];
394 return activeObject && activeObject.layer && activeObject.layer.id() === layer.id() && (typeof scrollRectIndex !== "number" || activeObject.scrollRectIndex === scrollRectIndex);
398 * @param {!WebInspector.Layer} layer
399 * @return {!WebInspector.Layers3DView.LayerStyle}
401 _styleForLayer: function(layer)
403 var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer);
404 var isHovered = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Hovered, layer);
407 borderColor = WebInspector.Layers3DView.SelectedBorderColor;
409 borderColor = WebInspector.Layers3DView.HoveredBorderColor;
411 borderColor = WebInspector.Layers3DView.BorderColor;
412 var borderWidth = isSelected ? WebInspector.Layers3DView.SelectedBorderWidth : WebInspector.Layers3DView.BorderWidth;
413 return {borderColor: borderColor, borderWidth: borderWidth};
417 * @param {!WebInspector.Layer} layer
420 _depthForLayer: function(layer)
422 return this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing;
426 * @param {!WebInspector.Layer} layer
427 * @param {number} index
430 _calculateScrollRectDepth: function(layer, index)
432 return this._depthForLayer(layer) + index * WebInspector.Layers3DView.ScrollRectSpacing + 1;
436 * @param {!WebInspector.Layer} layer
438 _calculateLayerRect: function(layer)
440 if (!this._isVisible[layer.id()])
442 var activeObject = WebInspector.Layers3DView.ActiveObject.createLayerActiveObject(layer);
443 var rect = new WebInspector.Layers3DView.Rectangle(activeObject);
444 var style = this._styleForLayer(layer);
445 rect.setVertices(layer.quad(), this._depthForLayer(layer));
446 rect.lineWidth = style.borderWidth;
447 rect.borderColor = style.borderColor;
448 this._rects.push(rect);
452 * @param {!WebInspector.Layer} layer
454 _calculateLayerScrollRects: function(layer)
456 var scrollRects = layer.scrollRects();
457 for (var i = 0; i < scrollRects.length; ++i) {
458 var activeObject = WebInspector.Layers3DView.ActiveObject.createScrollRectActiveObject(layer, i);
459 var rect = new WebInspector.Layers3DView.Rectangle(activeObject);
460 rect.calculateVerticesFromRect(layer, scrollRects[i].rect, this._calculateScrollRectDepth(layer, i));
461 var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer, i);
462 var color = isSelected ? WebInspector.Layers3DView.SelectedScrollRectBackgroundColor : WebInspector.Layers3DView.ScrollRectBackgroundColor;
463 rect.fillColor = color;
464 rect.borderColor = WebInspector.Layers3DView.ScrollRectBorderColor;
465 this._rects.push(rect);
470 * @param {!WebInspector.Layer} layer
472 _calculateLayerImageRect: function(layer)
474 var layerTexture = this._layerTexture;
475 if (layer.id() !== layerTexture.layerId)
477 var activeObject = WebInspector.Layers3DView.ActiveObject.createLayerActiveObject(layer);
478 var rect = new WebInspector.Layers3DView.Rectangle(activeObject);
479 rect.setVertices(layer.quad(), this._depthForLayer(layer));
480 rect.texture = layerTexture.texture;
481 this._rects.push(rect);
485 * @param {!WebInspector.Layer} layer
487 _calculateLayerTileRects: function(layer)
489 var tiles = this._textureManager.tilesForLayer(layer.id());
490 for (var i = 0; i < tiles.length; ++i) {
494 var activeObject = WebInspector.Layers3DView.ActiveObject.createTileActiveObject(layer, tile.traceEvent);
495 var rect = new WebInspector.Layers3DView.Rectangle(activeObject);
496 rect.calculateVerticesFromRect(layer, {x: tile.rect[0], y: tile.rect[1], width: tile.rect[2], height: tile.rect[3]}, this._depthForLayer(layer) + 1);
497 rect.texture = tile.texture;
498 this._rects.push(rect);
502 _calculateRects: function()
506 this._layerTree.forEachLayer(this._calculateLayerRect.bind(this));
508 if (this._showSlowScrollRectsSetting.get())
509 this._layerTree.forEachLayer(this._calculateLayerScrollRects.bind(this));
511 if (this._showPaintsSetting.get()) {
512 if (this._layerTexture)
513 this._layerTree.forEachLayer(this._calculateLayerImageRect.bind(this));
515 this._layerTree.forEachLayer(this._calculateLayerTileRects.bind(this));
520 * @param {!Array.<number>} color
521 * @return {!Array.<number>}
523 _makeColorsArray: function(color)
526 var normalizedColor = [color[0] / 255, color[1] / 255, color[2] / 255, color[3]];
527 for (var i = 0; i < 4; i++) {
528 colors = colors.concat(normalizedColor);
534 * @param {!Object} attribute
535 * @param {!Array.<number>} array
536 * @param {!number} length
538 _setVertexAttribute: function(attribute, array, length)
541 var buffer = gl.createBuffer();
542 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
543 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), gl.STATIC_DRAW);
544 gl.vertexAttribPointer(attribute, length, gl.FLOAT, false, 0, 0);
548 * @param {!Array.<number>} vertices
549 * @param {number} mode
550 * @param {!Array.<number>=} color
551 * @param {!Object=} texture
553 _drawRectangle: function(vertices, mode, color, texture)
556 var white = [255, 255, 255, 1];
557 this._setVertexAttribute(this._shaderProgram.vertexPositionAttribute, vertices, 3);
558 this._setVertexAttribute(this._shaderProgram.textureCoordAttribute, [0, 1, 1, 1, 1, 0, 0, 0], 2);
561 this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(white), white.length);
562 gl.activeTexture(gl.TEXTURE0);
563 gl.bindTexture(gl.TEXTURE_2D, texture);
564 gl.uniform1i(this._shaderProgram.samplerUniform, 0);
566 this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(color || white), color.length);
567 gl.bindTexture(gl.TEXTURE_2D, this._whiteTexture);
570 var numberOfVertices = vertices.length / 3;
571 gl.drawArrays(mode, 0, numberOfVertices);
575 * @param {!Array.<number>} vertices
576 * @param {!WebGLTexture} texture
578 _drawTexture: function(vertices, texture)
580 this._drawRectangle(vertices, this._gl.TRIANGLE_FAN, undefined, texture);
583 _drawViewportAndChrome: function()
585 var viewport = this._layerTree.viewportSize();
589 var drawChrome = !WebInspector.settings.frameViewerHideChromeWindow.get() && this._chromeTextures.length >= 3 && this._chromeTextures.indexOf(undefined) < 0;
590 var z = (this._maxDepth + 1) * WebInspector.Layers3DView.LayerSpacing;
591 var borderWidth = Math.ceil(WebInspector.Layers3DView.ViewportBorderWidth * this._scale);
592 var vertices = [viewport.width, 0, z, viewport.width, viewport.height, z, 0, viewport.height, z, 0, 0, z];
593 this._gl.lineWidth(borderWidth);
594 this._drawRectangle(vertices, drawChrome ? this._gl.LINE_STRIP : this._gl.LINE_LOOP, WebInspector.Layers3DView.ViewportBorderColor);
599 var borderAdjustment = WebInspector.Layers3DView.ViewportBorderWidth / 2;
600 var viewportWidth = this._layerTree.viewportSize().width + 2 * borderAdjustment;
601 var chromeHeight = this._chromeTextures[0].image.naturalHeight;
602 var middleFragmentWidth = viewportWidth - this._chromeTextures[0].image.naturalWidth - this._chromeTextures[2].image.naturalWidth;
603 var x = -borderAdjustment;
604 var y = -chromeHeight;
605 for (var i = 0; i < this._chromeTextures.length; ++i) {
606 var width = i === WebInspector.Layers3DView.ChromeTexture.Middle ? middleFragmentWidth : this._chromeTextures[i].image.naturalWidth;
607 if (width < 0 || x + width > viewportWidth)
609 vertices = [x, y, z, x + width, y, z, x + width, y + chromeHeight, z, x, y + chromeHeight, z];
610 this._drawTexture(vertices, /** @type {!WebGLTexture} */ (this._chromeTextures[i]));
616 * @param {!WebInspector.Layers3DView.Rectangle} rect
618 _drawViewRect: function(rect)
620 var vertices = rect.vertices;
622 this._drawTexture(vertices, rect.texture);
623 else if (rect.fillColor)
624 this._drawRectangle(vertices, this._gl.TRIANGLE_FAN, rect.fillColor);
625 this._gl.lineWidth(rect.lineWidth);
626 if (rect.borderColor)
627 this._drawRectangle(vertices, this._gl.LINE_LOOP, rect.borderColor);
632 if (!this.isShowing()) {
633 this._needsUpdate = true;
636 if (!this._layerTree || !this._layerTree.root()) {
637 this._emptyView.show(this.element);
640 this._emptyView.detach();
642 var gl = this._initGLIfNecessary();
643 this._resizeCanvas();
644 this._calculateDepths();
645 this._calculateRects();
646 this._updateTransformAndConstraints();
648 this._textureManager.setScale(Number.constrain(0.1, 1, this._scale));
649 gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
650 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
652 this._rects.forEach(this._drawViewRect.bind(this));
653 this._drawViewportAndChrome();
657 * @param {!Event} event
658 * @return {?WebInspector.Layers3DView.ActiveObject}
660 _activeObjectFromEventPoint: function(event)
662 if (!this._layerTree)
664 var closestIntersectionPoint = Infinity;
665 var closestObject = null;
666 var projectionMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0).multiply(this._projectionMatrix);
667 var x0 = (event.clientX - this._canvasElement.totalOffsetLeft()) * window.devicePixelRatio;
668 var y0 = -(event.clientY - this._canvasElement.totalOffsetTop()) * window.devicePixelRatio;
671 * @param {!WebInspector.Layers3DView.Rectangle} rect
673 function checkIntersection(rect)
675 if (!rect.relatedObject)
677 var t = rect.intersectWithLine(projectionMatrix, x0, y0);
678 if (t < closestIntersectionPoint) {
679 closestIntersectionPoint = t;
680 closestObject = rect.relatedObject;
684 this._rects.forEach(checkIntersection);
685 return closestObject;
689 * @param {string} caption
690 * @param {string} name
691 * @param {boolean} value
692 * @param {!Element} statusBarElement
693 * @return {!WebInspector.Setting}
695 _createVisibilitySetting: function(caption, name, value, statusBarElement)
697 var checkbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString(caption))
698 statusBarElement.appendChild(checkbox.element);
699 var setting = WebInspector.settings.createSetting(name, value)
700 WebInspector.SettingsUI.bindCheckbox(checkbox.inputElement, setting);
701 setting.addChangeListener(this._update, this);
705 _initStatusBar: function()
707 this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar");
708 this._panelStatusBarElement.appendChild(this._transformController.controlPanelElement());
709 this._showSlowScrollRectsSetting = this._createVisibilitySetting("Slow scroll rects", "frameViewerShowSlowScrollRects", true, this._panelStatusBarElement);
710 this._showPaintsSetting = this._createVisibilitySetting("Paints", "frameViewerShowPaints", true, this._panelStatusBarElement);
711 WebInspector.settings.frameViewerHideChromeWindow.addChangeListener(this._update, this);
715 * @param {!Event} event
717 _onContextMenu: function(event)
719 var activeObject = this._activeObjectFromEventPoint(event);
720 var node = activeObject && activeObject.layer && activeObject.layer.nodeForSelfOrAncestor();
721 var contextMenu = new WebInspector.ContextMenu(event);
722 contextMenu.appendItem(WebInspector.UIString("Reset View"), this._transformController.resetAndNotify.bind(this._transformController), false);
723 if (activeObject && activeObject.type() === WebInspector.Layers3DView.ActiveObject.Type.Tile)
724 contextMenu.appendItem(WebInspector.UIString("Show Paint Profiler"), this.dispatchEventToListeners.bind(this, WebInspector.Layers3DView.Events.PaintProfilerRequested, activeObject.traceEvent), false);
726 contextMenu.appendApplicableItems(node);
731 * @param {!Event} event
733 _onMouseMove: function(event)
737 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectHovered, this._activeObjectFromEventPoint(event));
741 * @param {!Event} event
743 _onMouseDown: function(event)
745 this._mouseDownX = event.clientX;
746 this._mouseDownY = event.clientY;
750 * @param {!Event} event
752 _onMouseUp: function(event)
754 const maxDistanceInPixels = 6;
755 if (this._mouseDownX && Math.abs(event.clientX - this._mouseDownX) < maxDistanceInPixels && Math.abs(event.clientY - this._mouseDownY) < maxDistanceInPixels)
756 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectSelected, this._activeObjectFromEventPoint(event));
757 delete this._mouseDownX;
758 delete this._mouseDownY;
762 * @param {!Event} event
764 _onDoubleClick: function(event)
766 var object = this._activeObjectFromEventPoint(event);
768 if (object.type() == WebInspector.Layers3DView.ActiveObject.Type.Tile)
769 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.PaintProfilerRequested, object.traceEvent);
770 else if (object.layer)
771 this.dispatchEventToListeners(WebInspector.Layers3DView.Events.LayerSnapshotRequested, object.layer);
773 event.stopPropagation();
776 __proto__: WebInspector.VBox.prototype
781 * @extends {WebInspector.Object}
783 WebInspector.LayerTextureManager = function()
785 WebInspector.Object.call(this);
789 WebInspector.LayerTextureManager.Events = {
790 TextureUpdated: "TextureUpated"
793 WebInspector.LayerTextureManager.prototype = {
796 /** @type {!Object.<string, !Array.<!WebInspector.LayerTextureManager.Tile>>} */
797 this._tilesByLayerId = {};
802 * @param {!WebGLRenderingContext} glContext
804 setContext: function(glContext)
806 this._gl = glContext;
808 this._updateTextures();
812 * @param {?Array.<!WebInspector.Layers3DView.PaintTile>} paintTiles
814 setTiles: function(paintTiles)
816 this._tilesByLayerId = {};
819 for (var i = 0; i < paintTiles.length; ++i) {
820 var layerId = paintTiles[i].layerId;
821 var tilesForLayer = this._tilesByLayerId[layerId];
822 if (!tilesForLayer) {
824 this._tilesByLayerId[layerId] = tilesForLayer;
826 var tile = new WebInspector.LayerTextureManager.Tile(paintTiles[i].snapshot, paintTiles[i].rect, paintTiles[i].traceEvent);
827 tilesForLayer.push(tile);
828 if (this._scale && this._gl)
829 this._updateTile(tile);
834 * @param {number} scale
836 setScale: function(scale)
838 if (this._scale && this._scale >= scale)
841 this._updateTextures();
845 * @param {string} layerId
846 * @return {!Array.<!WebInspector.LayerTextureManager.Tile>}
848 tilesForLayer: function(layerId)
850 return this._tilesByLayerId[layerId] || [];
853 _updateTextures: function()
860 for (var layerId in this._tilesByLayerId) {
861 for (var i = 0; i < this._tilesByLayerId[layerId].length; ++i) {
862 var tile = this._tilesByLayerId[layerId][i];
863 if (!tile.scale || tile.scale < this._scale)
864 this._updateTile(tile);
870 * @param {!WebInspector.LayerTextureManager.Tile} tile
872 _updateTile: function(tile)
874 console.assert(this._scale && this._gl);
875 tile.scale = this._scale;
876 tile.snapshot.requestImage(null, null, tile.scale, onGotImage.bind(this));
879 * @this {WebInspector.LayerTextureManager}
880 * @param {string=} imageURL
882 function onGotImage(imageURL)
885 this.createTexture(onTextureCreated.bind(this), imageURL);
889 * @this {WebInspector.LayerTextureManager}
890 * @param {?WebGLTexture} texture
892 function onTextureCreated(texture)
894 tile.texture = texture;
895 this.dispatchEventToListeners(WebInspector.LayerTextureManager.Events.TextureUpdated);
900 * @param {function(?WebGLTexture)} textureCreatedCallback
901 * @param {string} imageURL
903 createTexture: function(textureCreatedCallback, imageURL)
905 var image = new Image();
906 image.addEventListener("load", onImageLoaded.bind(this), false);
907 image.addEventListener("error", onImageError, false);
908 image.src = imageURL;
911 * @this {WebInspector.LayerTextureManager}
913 function onImageLoaded()
915 textureCreatedCallback(this._createTextureForImage(image));
918 function onImageError()
920 textureCreatedCallback(null);
925 * @param {!Image} image
926 * @return {!WebGLTexture} texture
928 _createTextureForImage: function(image)
930 var texture = this._gl.createTexture();
931 texture.image = image;
932 this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
933 this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, true);
934 this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, this._gl.RGBA, this._gl.UNSIGNED_BYTE, texture.image);
935 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, this._gl.LINEAR);
936 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, this._gl.LINEAR);
937 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, this._gl.CLAMP_TO_EDGE);
938 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, this._gl.CLAMP_TO_EDGE);
939 this._gl.bindTexture(this._gl.TEXTURE_2D, null);
943 __proto__: WebInspector.Object.prototype
948 * @param {?WebInspector.Layers3DView.ActiveObject} relatedObject
950 WebInspector.Layers3DView.Rectangle = function(relatedObject)
952 this.relatedObject = relatedObject;
953 /** @type {number} */
955 /** @type {?Array.<number>} */
956 this.borderColor = null;
957 /** @type {?Array.<number>} */
958 this.fillColor = null;
959 /** @type {?WebGLTexture} */
963 WebInspector.Layers3DView.Rectangle.prototype = {
965 * @param {!Array.<number>} quad
968 setVertices: function(quad, z)
970 this.vertices = [quad[0], quad[1], z, quad[2], quad[3], z, quad[4], quad[5], z, quad[6], quad[7], z];
974 * Finds coordinates of point on layer quad, having offsets (ratioX * width) and (ratioY * height)
975 * from the left corner of the initial layer rect, where width and heigth are layer bounds.
976 * @param {!Array.<number>} quad
977 * @param {number} ratioX
978 * @param {number} ratioY
979 * @return {!Array.<number>}
981 _calculatePointOnQuad: function(quad, ratioX, ratioY)
991 // Point on the first quad side clockwise
992 var firstSidePointX = x0 + ratioX * (x1 - x0);
993 var firstSidePointY = y0 + ratioX * (y1 - y0);
994 // Point on the third quad side clockwise
995 var thirdSidePointX = x3 + ratioX * (x2 - x3);
996 var thirdSidePointY = y3 + ratioX * (y2 - y3);
997 var x = firstSidePointX + ratioY * (thirdSidePointX - firstSidePointX);
998 var y = firstSidePointY + ratioY * (thirdSidePointY - firstSidePointY);
1003 * @param {!WebInspector.Layer} layer
1004 * @param {!DOMAgent.Rect} rect
1007 calculateVerticesFromRect: function(layer, rect, z)
1009 var quad = layer.quad();
1010 var rx1 = rect.x / layer.width();
1011 var rx2 = (rect.x + rect.width) / layer.width();
1012 var ry1 = rect.y / layer.height();
1013 var ry2 = (rect.y + rect.height) / layer.height();
1014 var rectQuad = this._calculatePointOnQuad(quad, rx1, ry1).concat(this._calculatePointOnQuad(quad, rx2, ry1))
1015 .concat(this._calculatePointOnQuad(quad, rx2, ry2)).concat(this._calculatePointOnQuad(quad, rx1, ry2));
1016 this.setVertices(rectQuad, z);
1020 * Intersects quad with given transform matrix and line l(t) = (x0, y0, t)
1021 * @param {!CSSMatrix} matrix
1022 * @param {!number} x0
1023 * @param {!number} y0
1024 * @return {(number|undefined)}
1026 intersectWithLine: function(matrix, x0, y0)
1030 // Vertices of the quad with transform matrix applied
1032 for (i = 0; i < 4; ++i)
1033 points[i] = WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(new WebInspector.Geometry.Vector(this.vertices[i * 3], this.vertices[i * 3 + 1], this.vertices[i * 3 + 2]), matrix);
1034 // Calculating quad plane normal
1035 var normal = WebInspector.Geometry.crossProduct(WebInspector.Geometry.subtract(points[1], points[0]), WebInspector.Geometry.subtract(points[2], points[1]));
1036 // General form of the equation of the quad plane: A * x + B * y + C * z + D = 0
1040 var D = -(A * points[0].x + B * points[0].y + C * points[0].z);
1041 // Finding t from the equation
1042 var t = -(D + A * x0 + B * y0) / C;
1043 // Point of the intersection
1044 var pt = new WebInspector.Geometry.Vector(x0, y0, t);
1045 // Vectors from the intersection point to vertices of the quad
1046 var tVects = points.map(WebInspector.Geometry.subtract.bind(null, pt));
1047 // Intersection point lies inside of the polygon if scalar products of normal of the plane and
1048 // cross products of successive tVects are all nonstrictly above or all nonstrictly below zero
1049 for (i = 0; i < tVects.length; ++i) {
1050 var product = WebInspector.Geometry.scalarProduct(normal, WebInspector.Geometry.crossProduct(tVects[i], tVects[(i + 1) % tVects.length]));
1061 WebInspector.Layers3DView.ActiveObject = function()
1068 WebInspector.Layers3DView.ActiveObject.Type = {
1070 ScrollRect: "ScrollRect",
1075 * @param {!WebInspector.Layer} layer
1076 * @return {!WebInspector.Layers3DView.ActiveObject}
1078 WebInspector.Layers3DView.ActiveObject.createLayerActiveObject = function(layer)
1080 var activeObject = new WebInspector.Layers3DView.ActiveObject();
1081 activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.Layer;
1082 activeObject.layer = layer;
1083 return activeObject;
1087 * @param {!WebInspector.Layer} layer
1088 * @param {number} scrollRectIndex
1089 * @return {!WebInspector.Layers3DView.ActiveObject}
1091 WebInspector.Layers3DView.ActiveObject.createScrollRectActiveObject = function(layer, scrollRectIndex)
1093 var activeObject = new WebInspector.Layers3DView.ActiveObject();
1094 activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.ScrollRect;
1095 activeObject.layer = layer;
1096 activeObject.scrollRectIndex = scrollRectIndex;
1097 return activeObject;
1101 * @param {!WebInspector.Layer} layer
1102 * @param {!WebInspector.TracingModel.Event} traceEvent
1103 * @return {!WebInspector.Layers3DView.ActiveObject}
1105 WebInspector.Layers3DView.ActiveObject.createTileActiveObject = function(layer, traceEvent)
1107 var activeObject = new WebInspector.Layers3DView.ActiveObject();
1108 activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.Tile;
1109 activeObject.layer = layer;
1110 activeObject.traceEvent = traceEvent;
1111 return activeObject;
1114 WebInspector.Layers3DView.ActiveObject.prototype = {
1116 * @return {!WebInspector.Layers3DView.ActiveObject.Type}
1126 * @param {!WebInspector.PaintProfilerSnapshot} snapshot
1127 * @param {!Array.<number>} rect
1128 * @param {!WebInspector.TracingModel.Event} traceEvent
1130 WebInspector.LayerTextureManager.Tile = function(snapshot, rect, traceEvent)
1132 this.snapshot = snapshot;
1134 this.traceEvent = traceEvent;
1136 /** @type {?WebGLTexture} */
1137 this.texture = null;