Merge "Add occlusion state for surface flinger trace" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
12623eba4c
@@ -200,4 +200,11 @@ function multiply_rect(matrix, rect) {
|
|||||||
return outrect;
|
return outrect;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {format_transform_type, fill_transform_data, is_simple_transform, multiply_rect};
|
// Returns true if the applying the transform on an an axis aligned rectangle
|
||||||
|
// results in another axis aligned rectangle.
|
||||||
|
function is_simple_rotation(transform) {
|
||||||
|
return !is_type_flag_set(transform, ROT_INVALID_VAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {format_transform_type, fill_transform_data, is_simple_transform,
|
||||||
|
multiply_rect, is_simple_rotation};
|
||||||
299
tools/winscope/src/sf_visibility.js
Normal file
299
tools/winscope/src/sf_visibility.js
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for deriving state and visibility from the hierarchy. This
|
||||||
|
* duplicates some of the logic in surface flinger. If the trace contains
|
||||||
|
* composition state (visibleRegion), it will be used otherwise it will be
|
||||||
|
* derived.
|
||||||
|
*/
|
||||||
|
import { multiply_rect, is_simple_rotation } from './matrix_utils.js'
|
||||||
|
|
||||||
|
// Layer flags
|
||||||
|
const FLAG_HIDDEN = 0x01;
|
||||||
|
const FLAG_OPAQUE = 0x02;
|
||||||
|
const FLAG_SECURE = 0x80;
|
||||||
|
|
||||||
|
function flags_to_string(flags) {
|
||||||
|
if (!flags) return '';
|
||||||
|
var verboseFlags = [];
|
||||||
|
if (flags & FLAG_HIDDEN) verboseFlags.push("HIDDEN");
|
||||||
|
if (flags & FLAG_OPAQUE) verboseFlags.push("OPAQUE");
|
||||||
|
if (flags & FLAG_SECURE) verboseFlags.push("SECURE");
|
||||||
|
return verboseFlags.join('|') + " (" + flags + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_empty(region) {
|
||||||
|
return region == undefined ||
|
||||||
|
region.rect == undefined ||
|
||||||
|
region.rect.length == 0 ||
|
||||||
|
region.rect.every(function(r) { return is_empty_rect(r) } );
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_empty_rect(rect) {
|
||||||
|
var right = rect.right || 0;
|
||||||
|
var left = rect.left || 0;
|
||||||
|
var top = rect.top || 0;
|
||||||
|
var bottom = rect.bottom || 0;
|
||||||
|
|
||||||
|
return (right - left) <= 0 || (bottom - top) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_rect_empty_and_valid(rect) {
|
||||||
|
return rect &&
|
||||||
|
(rect.left - rect.right === 0 || rect.top - rect.bottom === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transformation matrix is defined as the product of:
|
||||||
|
* | cos(a) -sin(a) | \/ | X 0 |
|
||||||
|
* | sin(a) cos(a) | /\ | 0 Y |
|
||||||
|
*
|
||||||
|
* where a is a rotation angle, and X and Y are scaling factors.
|
||||||
|
* A transformation matrix is invalid when either X or Y is zero,
|
||||||
|
* as a rotation matrix is valid for any angle. When either X or Y
|
||||||
|
* is 0, then the scaling matrix is not invertible, which makes the
|
||||||
|
* transformation matrix not invertible as well. A 2D matrix with
|
||||||
|
* components | A B | is not invertible if and only if AD - BC = 0.
|
||||||
|
* | C D |
|
||||||
|
* This check is included above.
|
||||||
|
*/
|
||||||
|
function is_transform_invalid(transform) {
|
||||||
|
return !transform || (transform.dsdx * transform.dtdy ===
|
||||||
|
transform.dtdx * transform.dsdy); //determinant of transform
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_opaque(layer) {
|
||||||
|
return layer.color == undefined || (layer.color.a || 0) < 1 || layer.isOpaque;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fills_color(layer) {
|
||||||
|
return layer.color && layer.color.a > 0 &&
|
||||||
|
layer.color.r >= 0 && layer.color.g >= 0 &&
|
||||||
|
layer.color.b >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function draws_shadows(layer) {
|
||||||
|
return layer.shadowRadius && layer.shadowRadius > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function has_blur(layer) {
|
||||||
|
return layer.backgroundBlurRadius && layer.backgroundBlurRadius > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function has_effects(layer) {
|
||||||
|
// Support previous color layer
|
||||||
|
if (layer.type === 'ColorLayer') return true;
|
||||||
|
|
||||||
|
// Support newer effect layer
|
||||||
|
return layer.type === 'EffectLayer' &&
|
||||||
|
(fills_color(layer) || draws_shadows(layer) || has_blur(layer))
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_hidden_by_policy(layer) {
|
||||||
|
return layer.flags & FLAG_HIDDEN == FLAG_HIDDEN ||
|
||||||
|
// offscreen layer root has a unique layer id
|
||||||
|
layer.id == 0x7FFFFFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the layer is visible based on its visibleRegion if available
|
||||||
|
* or its type, active buffer content, alpha and properties.
|
||||||
|
*/
|
||||||
|
function is_visible(layer, hiddenByPolicy, includesCompositionState) {
|
||||||
|
|
||||||
|
if (includesCompositionState) {
|
||||||
|
return !is_empty(layer.visibleRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hiddenByPolicy) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!layer.activeBuffer && !has_effects(layer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!layer.color || !layer.color.a || layer.color.a == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer.occludedBy && layer.occludedBy.length > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!layer.bounds || is_empty_rect(layer.bounds)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_visibility_reason(layer) {
|
||||||
|
if (layer.type === 'ContainerLayer') {
|
||||||
|
return 'ContainerLayer';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_hidden_by_policy(layer)) {
|
||||||
|
return 'Flag is hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer.hidden) {
|
||||||
|
return 'Hidden by parent';
|
||||||
|
}
|
||||||
|
|
||||||
|
let isBufferLayer = (layer.type === 'BufferStateLayer' || layer.type === 'BufferQueueLayer');
|
||||||
|
if (isBufferLayer && (!layer.activeBuffer ||
|
||||||
|
layer.activeBuffer.height === 0 || layer.activeBuffer.width === 0)) {
|
||||||
|
return 'Buffer is empty';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!layer.color || !layer.color.a || layer.color.a == 0) {
|
||||||
|
return 'Alpha is 0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_rect_empty_and_valid(layer.crop)) {
|
||||||
|
return 'Crop is 0x0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!layer.bounds || is_empty_rect(layer.bounds)) {
|
||||||
|
return 'Bounds is 0x0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_transform_invalid(layer.transform)) {
|
||||||
|
return 'Transform is invalid';
|
||||||
|
}
|
||||||
|
if (layer.isRelativeOf && layer.zOrderRelativeOf == -1) {
|
||||||
|
return 'RelativeOf layer has been removed';
|
||||||
|
}
|
||||||
|
|
||||||
|
let isEffectLayer = (layer.type === 'EffectLayer');
|
||||||
|
if (isEffectLayer && !fills_color(layer) && !draws_shadows(layer) && !has_blur(layer)) {
|
||||||
|
return 'Effect layer does not have color fill, shadow or blur';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer.occludedBy && layer.occludedBy.length > 0) {
|
||||||
|
return 'Layer is occluded by:' + layer.occludedBy.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer.visible) {
|
||||||
|
return "Unknown";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if rectA overlaps rectB
|
||||||
|
function overlaps(rectA, rectB) {
|
||||||
|
return rectA.left < rectB.right && rectA.right > rectB.left &&
|
||||||
|
rectA.top < rectB.bottom && rectA.bottom > rectA.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if outer rect contains inner rect
|
||||||
|
function contains(outerLayer, innerLayer) {
|
||||||
|
if (!is_simple_rotation(outerLayer.transform) || !is_simple_rotation(innerLayer.transform)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const outer = screen_bounds(outerLayer);
|
||||||
|
const inner = screen_bounds(innerLayer);
|
||||||
|
return inner.left >= outer.left && inner.top >= outer.top &&
|
||||||
|
inner.right <= outer.right && inner.bottom <= outer.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
function screen_bounds(layer) {
|
||||||
|
if (layer.screenBounds) return layer.screenBounds;
|
||||||
|
let transformMatrix = layer.transform;
|
||||||
|
var tx = layer.position ? layer.position.x || 0 : 0;
|
||||||
|
var ty = layer.position ? layer.position.y || 0 : 0;
|
||||||
|
|
||||||
|
transformMatrix.tx = tx
|
||||||
|
transformMatrix.ty = ty
|
||||||
|
return multiply_rect(transformMatrix, layer.bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse in z-order from top to bottom and fill in occlusion data
|
||||||
|
function fill_occlusion_state(layerMap, rootLayers, includesCompositionState) {
|
||||||
|
const layers = rootLayers.filter(layer => !layer.isRelativeOf);
|
||||||
|
traverse_top_to_bottom(layerMap, layers, {opaqueRects:[], transparentRects:[], screenBounds:null}, (layer, globalState) => {
|
||||||
|
|
||||||
|
if (layer.name.startsWith("Root#0") && layer.sourceBounds) {
|
||||||
|
globalState.screenBounds = {left:0, top:0, bottom:layer.sourceBounds.bottom, right:layer.sourceBounds.right};
|
||||||
|
}
|
||||||
|
|
||||||
|
const visible = is_visible(layer, layer.hidden, includesCompositionState);
|
||||||
|
if (visible) {
|
||||||
|
let fullyOccludes = (testLayer) => contains(testLayer, layer);
|
||||||
|
let partiallyOccludes = (testLayer) => overlaps(screen_bounds(testLayer), screen_bounds(layer));
|
||||||
|
let covers = (testLayer) => overlaps(screen_bounds(testLayer), screen_bounds(layer));
|
||||||
|
|
||||||
|
layer.occludedBy = globalState.opaqueRects.filter(fullyOccludes).map(layer => layer.id);
|
||||||
|
layer.partiallyOccludedBy = globalState.opaqueRects.filter(partiallyOccludes).map(layer => layer.id);
|
||||||
|
layer.coveredBy = globalState.transparentRects.filter(covers).map(layer => layer.id);
|
||||||
|
|
||||||
|
if (is_opaque(layer)) {
|
||||||
|
globalState.opaqueRects.push(layer);
|
||||||
|
} else {
|
||||||
|
globalState.transparentRects.push(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.visible = is_visible(layer, layer.hidden, includesCompositionState);
|
||||||
|
if (!layer.visible) {
|
||||||
|
layer.invisibleDueTo = get_visibility_reason(layer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverse_top_to_bottom(layerMap, rootLayers, globalState, fn) {
|
||||||
|
for (var i = rootLayers.length-1; i >=0; i--) {
|
||||||
|
const relatives = rootLayers[i].relatives.map(id => layerMap[id]);
|
||||||
|
const children = rootLayers[i].children.map(id => layerMap[id])
|
||||||
|
|
||||||
|
// traverse through relatives and children that are not relatives
|
||||||
|
const traverseList = relatives.concat(children.filter(layer => !layer.isRelativeOf));
|
||||||
|
traverseList.sort((lhs, rhs) => rhs.z - lhs.z);
|
||||||
|
|
||||||
|
traverseList.filter((layer) => layer.z >=0).forEach(layer => {
|
||||||
|
traverse_top_to_bottom(layerMap, [layer], globalState, fn);
|
||||||
|
});
|
||||||
|
|
||||||
|
fn(rootLayers[i], globalState);
|
||||||
|
|
||||||
|
traverseList.filter((layer) => layer.z < 0).forEach(layer => {
|
||||||
|
traverse_top_to_bottom(layerMap, [layer], globalState, fn);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse all children and fill in any inherited states.
|
||||||
|
function fill_inherited_state(layerMap, rootLayers) {
|
||||||
|
traverse(layerMap, rootLayers, (layer, parent) => {
|
||||||
|
const parentHidden = parent && parent.hidden;
|
||||||
|
layer.hidden = is_hidden_by_policy(layer) || parentHidden;
|
||||||
|
layer.verboseFlags = flags_to_string(layer.flags);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverse(layerMap, rootLayers, fn) {
|
||||||
|
for (var i = rootLayers.length-1; i >=0; i--) {
|
||||||
|
const parentId = rootLayers[i].parent;
|
||||||
|
const parent = parentId == -1 ? null : layerMap[parentId];
|
||||||
|
fn(rootLayers[i], parent);
|
||||||
|
const children = rootLayers[i].children.map(id => layerMap[id]);
|
||||||
|
traverse(layerMap, children, fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {fill_occlusion_state, fill_inherited_state};
|
||||||
@@ -15,11 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {transform, nanos_to_string, get_visible_chip} from './transform.js'
|
import {transform, nanos_to_string, get_visible_chip} from './transform.js'
|
||||||
|
import { fill_occlusion_state, fill_inherited_state } from './sf_visibility.js';
|
||||||
// Layer flags
|
|
||||||
const FLAG_HIDDEN = 0x01;
|
|
||||||
const FLAG_OPAQUE = 0x02;
|
|
||||||
const FLAG_SECURE = 0x80;
|
|
||||||
|
|
||||||
var RELATIVE_Z_CHIP = {short: 'RelZ',
|
var RELATIVE_Z_CHIP = {short: 'RelZ',
|
||||||
long: "Is relative Z-ordered to another surface",
|
long: "Is relative Z-ordered to another surface",
|
||||||
@@ -37,60 +33,7 @@ var HWC_CHIP = {short: 'HWC',
|
|||||||
long: "This layer was composed by Hardware Composer",
|
long: "This layer was composed by Hardware Composer",
|
||||||
class: 'hwc'};
|
class: 'hwc'};
|
||||||
|
|
||||||
function transform_layer(layer, {parentBounds, parentHidden}) {
|
function transform_layer(layer) {
|
||||||
function get_size(layer) {
|
|
||||||
var size = layer.size || {w: 0, h: 0};
|
|
||||||
return {
|
|
||||||
left: 0,
|
|
||||||
right: size.w,
|
|
||||||
top: 0,
|
|
||||||
bottom: size.h
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_crop(layer) {
|
|
||||||
var crop = layer.crop || {left: 0, top: 0, right: 0 , bottom:0};
|
|
||||||
return {
|
|
||||||
left: crop.left || 0,
|
|
||||||
right: crop.right || 0,
|
|
||||||
top: crop.top || 0,
|
|
||||||
bottom: crop.bottom || 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function intersect(bounds, crop) {
|
|
||||||
return {
|
|
||||||
left: Math.max(crop.left, bounds.left),
|
|
||||||
right: Math.min(crop.right, bounds.right),
|
|
||||||
top: Math.max(crop.top, bounds.top),
|
|
||||||
bottom: Math.min(crop.bottom, bounds.bottom),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_empty_rect(rect) {
|
|
||||||
var right = rect.right || 0;
|
|
||||||
var left = rect.left || 0;
|
|
||||||
var top = rect.top || 0;
|
|
||||||
var bottom = rect.bottom || 0;
|
|
||||||
|
|
||||||
return (right - left) <= 0 || (bottom - top) <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_cropped_bounds(layer, parentBounds) {
|
|
||||||
var size = get_size(layer);
|
|
||||||
var crop = get_crop(layer);
|
|
||||||
if (!is_empty_rect(size) && !is_empty_rect(crop)) {
|
|
||||||
return intersect(size, crop);
|
|
||||||
}
|
|
||||||
if (!is_empty_rect(size)) {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
if (!is_empty_rect(crop)) {
|
|
||||||
return crop;
|
|
||||||
}
|
|
||||||
return parentBounds || { left: 0, right: 0, top: 0, bottom: 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
function offset_to(bounds, x, y) {
|
function offset_to(bounds, x, y) {
|
||||||
return {
|
return {
|
||||||
right: bounds.right - (bounds.left - x),
|
right: bounds.right - (bounds.left - x),
|
||||||
@@ -100,125 +43,30 @@ function transform_layer(layer, {parentBounds, parentHidden}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function transform_bounds(layer, parentBounds) {
|
function get_rect(layer) {
|
||||||
var result = layer.bounds || get_cropped_bounds(layer, parentBounds);
|
var result = layer.bounds;
|
||||||
var tx = (layer.position) ? layer.position.x || 0 : 0;
|
var tx = layer.position ? layer.position.x || 0 : 0;
|
||||||
var ty = (layer.position) ? layer.position.y || 0 : 0;
|
var ty = layer.position ? layer.position.y || 0 : 0;
|
||||||
result = offset_to(result, 0, 0);
|
result = offset_to(result, 0, 0);
|
||||||
result.label = layer.name;
|
result.label = layer.name;
|
||||||
result.transform = layer.transform || {dsdx:1, dtdx:0, dsdy:0, dtdy:1};
|
result.transform = layer.transform;
|
||||||
result.transform.tx = tx;
|
result.transform.tx = tx;
|
||||||
result.transform.ty = ty;
|
result.transform.ty = ty;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_opaque(layer) {
|
|
||||||
return layer.color == undefined || (layer.color.a || 0) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_empty(region) {
|
|
||||||
return region == undefined ||
|
|
||||||
region.rect == undefined ||
|
|
||||||
region.rect.length == 0 ||
|
|
||||||
region.rect.every(function(r) { return is_empty_rect(r) } );
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_rect_empty_and_valid(rect) {
|
|
||||||
return rect &&
|
|
||||||
(rect.left - rect.right === 0 || rect.top - rect.bottom === 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_transform_invalid(transform) {
|
|
||||||
return !transform || (transform.dsdx * transform.dtdy ===
|
|
||||||
transform.dtdx * transform.dsdy); //determinant of transform
|
|
||||||
/**
|
|
||||||
* The transformation matrix is defined as the product of:
|
|
||||||
* | cos(a) -sin(a) | \/ | X 0 |
|
|
||||||
* | sin(a) cos(a) | /\ | 0 Y |
|
|
||||||
*
|
|
||||||
* where a is a rotation angle, and X and Y are scaling factors.
|
|
||||||
* A transformation matrix is invalid when either X or Y is zero,
|
|
||||||
* as a rotation matrix is valid for any angle. When either X or Y
|
|
||||||
* is 0, then the scaling matrix is not invertible, which makes the
|
|
||||||
* transformation matrix not invertible as well. A 2D matrix with
|
|
||||||
* components | A B | is uninvertible if and only if AD - BC = 0.
|
|
||||||
* | C D |
|
|
||||||
* This check is included above.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
function fills_color(layer) {
|
|
||||||
return layer.color && layer.color.a > 0 &&
|
|
||||||
layer.color.r >= 0 && layer.color.g >= 0 &&
|
|
||||||
layer.color.b >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function draws_shadows(layer) {
|
|
||||||
return layer.shadowRadius && layer.shadowRadius > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function has_effects(layer) {
|
|
||||||
// Support previous color layer
|
|
||||||
if (layer.type === 'ColorLayer') return true;
|
|
||||||
|
|
||||||
// Support newer effect layer
|
|
||||||
return layer.type === 'EffectLayer' &&
|
|
||||||
(fills_color(layer) || draws_shadows(layer))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the layer is visible on screen according to its type,
|
|
||||||
* active buffer content, alpha and visible regions.
|
|
||||||
*
|
|
||||||
* @param {layer} layer
|
|
||||||
* @returns if the layer is visible on screen or not
|
|
||||||
*/
|
|
||||||
function is_visible(layer) {
|
|
||||||
var visible = (layer.activeBuffer || has_effects(layer))
|
|
||||||
&& !hidden && is_opaque(layer);
|
|
||||||
visible &= !is_empty(layer.visibleRegion);
|
|
||||||
return visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
function add_hwc_composition_type_chip(layer) {
|
function add_hwc_composition_type_chip(layer) {
|
||||||
if (layer.hwcCompositionType === "CLIENT") {
|
if (layer.hwcCompositionType === "CLIENT") {
|
||||||
chips.push(GPU_CHIP);
|
chips.push(GPU_CHIP);
|
||||||
} else if (layer.hwcCompositionType === "DEVICE") {
|
} else if (layer.hwcCompositionType === "DEVICE" || layer.hwcCompositionType === "SOLID_COLOR") {
|
||||||
chips.push(HWC_CHIP);
|
chips.push(HWC_CHIP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function postprocess_flags(layer) {
|
|
||||||
if (!layer.flags) return;
|
|
||||||
var verboseFlags = [];
|
|
||||||
if (layer.flags & FLAG_HIDDEN) {
|
|
||||||
verboseFlags.push("HIDDEN");
|
|
||||||
}
|
|
||||||
if (layer.flags & FLAG_OPAQUE) {
|
|
||||||
verboseFlags.push("OPAQUE");
|
|
||||||
}
|
|
||||||
if (layer.flags & FLAG_SECURE) {
|
|
||||||
verboseFlags.push("SECURE");
|
|
||||||
}
|
|
||||||
|
|
||||||
layer.flags = verboseFlags.join('|') + " (" + layer.flags + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
var chips = [];
|
var chips = [];
|
||||||
var rect = transform_bounds(layer, parentBounds);
|
if (layer.visible) {
|
||||||
var hidden = (layer.flags & FLAG_HIDDEN) != 0 || parentHidden;
|
|
||||||
var visible = is_visible(layer);
|
|
||||||
if (visible) {
|
|
||||||
chips.push(get_visible_chip());
|
chips.push(get_visible_chip());
|
||||||
} else {
|
|
||||||
rect = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var bounds = undefined;
|
|
||||||
if (layer.name.startsWith("Display Root#0") && layer.sourceBounds) {
|
|
||||||
bounds = {width: layer.sourceBounds.right, height: layer.sourceBounds.bottom};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((layer.zOrderRelativeOf || -1) !== -1) {
|
if ((layer.zOrderRelativeOf || -1) !== -1) {
|
||||||
chips.push(RELATIVE_Z_CHIP);
|
chips.push(RELATIVE_Z_CHIP);
|
||||||
}
|
}
|
||||||
@@ -228,61 +76,22 @@ function transform_layer(layer, {parentBounds, parentHidden}) {
|
|||||||
if (layer.missing) {
|
if (layer.missing) {
|
||||||
chips.push(MISSING_LAYER);
|
chips.push(MISSING_LAYER);
|
||||||
}
|
}
|
||||||
function visibilityReason(layer) {
|
|
||||||
var reasons = [];
|
|
||||||
if (!layer.color || layer.color.a === 0) {
|
|
||||||
reasons.push('Alpha is 0');
|
|
||||||
}
|
|
||||||
if (layer.flags && (layer.flags & FLAG_HIDDEN != 0)) {
|
|
||||||
reasons.push('Flag is hidden');
|
|
||||||
}
|
|
||||||
if (is_rect_empty_and_valid(layer.crop)) {
|
|
||||||
reasons.push('Crop is zero');
|
|
||||||
}
|
|
||||||
if (is_transform_invalid(layer.transform)) {
|
|
||||||
reasons.push('Transform is invalid');
|
|
||||||
}
|
|
||||||
if (layer.isRelativeOf && layer.zOrderRelativeOf == -1) {
|
|
||||||
reasons.push('RelativeOf layer has been removed');
|
|
||||||
}
|
|
||||||
return reasons.join();
|
|
||||||
}
|
|
||||||
if (parentHidden) {
|
|
||||||
layer.invisibleDueTo = 'Hidden by parent with ID: ' + parentHidden;
|
|
||||||
} else {
|
|
||||||
let reasons_hidden = visibilityReason(layer);
|
|
||||||
let isBufferLayer = (layer.type === 'BufferStateLayer' || layer.type === 'BufferQueueLayer');
|
|
||||||
if (reasons_hidden) {
|
|
||||||
layer.invisibleDueTo = reasons_hidden;
|
|
||||||
parentHidden = layer.id
|
|
||||||
} else if (layer.type === 'ContainerLayer') {
|
|
||||||
layer.invisibleDueTo = 'This is a ContainerLayer.';
|
|
||||||
} else if (isBufferLayer && (!layer.activeBuffer ||
|
|
||||||
layer.activeBuffer.height === 0 || layer.activeBuffer.width === 0)) {
|
|
||||||
layer.invisibleDueTo = 'The buffer is empty.';
|
|
||||||
} else if (!visible) {
|
|
||||||
layer.invisibleDueTo = 'Unknown. Occluded by another layer?';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var transform_layer_with_parent_hidden =
|
|
||||||
(layer) => transform_layer(layer, {parentBounds: rect, parentHidden: parentHidden});
|
|
||||||
postprocess_flags(layer);
|
|
||||||
add_hwc_composition_type_chip(layer);
|
add_hwc_composition_type_chip(layer);
|
||||||
|
const rect = layer.visible ? get_rect(layer) : undefined;
|
||||||
|
|
||||||
return transform({
|
return transform({
|
||||||
obj: layer,
|
obj: layer,
|
||||||
kind: '',
|
kind: '',
|
||||||
name: layer.id + ": " + layer.name,
|
name: layer.id + ": " + layer.name,
|
||||||
children: [
|
children: [[layer.resolvedChildren, transform_layer]],
|
||||||
[layer.resolvedChildren, transform_layer_with_parent_hidden],
|
|
||||||
],
|
|
||||||
rect,
|
rect,
|
||||||
bounds,
|
undefined /* bounds */,
|
||||||
highlight: rect,
|
highlight: rect,
|
||||||
chips,
|
chips,
|
||||||
visible,
|
visible: layer.visible,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function missingLayer(childId) {
|
function missingLayer(childId) {
|
||||||
return {
|
return {
|
||||||
name: "layer #" + childId,
|
name: "layer #" + childId,
|
||||||
@@ -292,7 +101,7 @@ function missingLayer(childId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function transform_layers(layers) {
|
function transform_layers(includesCompositionState, layers) {
|
||||||
var idToItem = {};
|
var idToItem = {};
|
||||||
var isChild = {}
|
var isChild = {}
|
||||||
|
|
||||||
@@ -316,7 +125,8 @@ function transform_layers(layers) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var roots = layersList.filter((e) => !isChild[e.id]);
|
var roots = layersList.filter((e) => !isChild[e.id]);
|
||||||
|
fill_inherited_state(idToItem, roots);
|
||||||
|
fill_occlusion_state(idToItem, roots, includesCompositionState);
|
||||||
function foreachTree(nodes, fun) {
|
function foreachTree(nodes, fun) {
|
||||||
nodes.forEach((n) => {
|
nodes.forEach((n) => {
|
||||||
fun(n);
|
fun(n);
|
||||||
@@ -358,12 +168,13 @@ function transform_layers(layers) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function transform_layers_entry(entry) {
|
function transform_layers_entry(entry) {
|
||||||
|
const includesCompositionState = !entry.excludesCompositionState;
|
||||||
return transform({
|
return transform({
|
||||||
obj: entry,
|
obj: entry,
|
||||||
kind: 'entry',
|
kind: 'entry',
|
||||||
name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
|
name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
|
||||||
children: [
|
children: [
|
||||||
[[entry.layers], transform_layers],
|
[[entry.layers], (layer) => transform_layers(includesCompositionState, layer)],
|
||||||
],
|
],
|
||||||
timestamp: entry.elapsedRealtimeNanos,
|
timestamp: entry.elapsedRealtimeNanos,
|
||||||
stableId: 'entry',
|
stableId: 'entry',
|
||||||
|
|||||||
Reference in New Issue
Block a user