Commit e4f43999 authored by caseq@chromium.org's avatar caseq@chromium.org

Timeline: add preview of painted picture for Paint event

- enable "Capture picture" check-box in Timeline capture filter when
    Timeline on trace events experiment is enabled;
- add disabled-by-default-devtools.timeline.{picture,layers} categories
    to trace filter when the above is checked;
- associate recorded SkPicture with the last paint event of the layer
    with matching id;
- display pictures preview in Timeline's details view, when these are
    available.

BUG=

Review URL: https://codereview.chromium.org/328143003

git-svn-id: svn://svn.chromium.org/blink/trunk@176196 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 31e41790
......@@ -142,6 +142,19 @@ WebInspector.TimelineModel.forAllRecords = function(recordsArray, preOrderCallba
}
WebInspector.TimelineModel.prototype = {
/**
* @param {boolean} captureStacks
* @param {boolean} captureMemory
* @param {boolean} capturePictures
*/
startRecording: function(captureStacks, captureMemory, capturePictures)
{
},
stopRecording: function()
{
},
/**
* @return {boolean}
*/
......
......@@ -36,9 +36,11 @@ WebInspector.TimelineModelImpl.prototype = {
/**
* @param {boolean} captureStacks
* @param {boolean} captureMemory
* @param {boolean} capturePictures
*/
startRecording: function(captureStacks, captureMemory)
startRecording: function(captureStacks, captureMemory, capturePictures)
{
console.assert(!capturePictures, "Legacy timeline does not support capturing pictures");
this._clientInitiatedRecording = true;
this.reset();
var maxStackFrames = captureStacks ? 30 : 0;
......
......@@ -341,6 +341,11 @@ WebInspector.TimelinePanel.prototype = {
this._captureTracingSetting, true, undefined,
WebInspector.UIString("Capture tracing information")));
this._captureTracingSetting.addChangeListener(this._onModeChanged, this);
} else if (WebInspector.experimentsSettings.timelineOnTraceEvents.isEnabled()) {
this._captureLayersAndPicturesSetting = WebInspector.settings.createSetting("timelineCaptureLayersAndPictures", false);
topPaneSidebarElement.appendChild(WebInspector.SettingsUI.createSettingCheckbox(WebInspector.UIString("Capture pictures"),
this._captureLayersAndPicturesSetting, true, undefined,
WebInspector.UIString("Capture graphics layer positions and painted pictures")));
}
},
......@@ -652,7 +657,7 @@ WebInspector.TimelinePanel.prototype = {
_startRecording: function(userInitiated)
{
this._userInitiatedRecording = userInitiated;
this._model.startRecording(this._captureStacksSetting.get(), this._captureMemorySetting.get());
this._model.startRecording(this._captureStacksSetting.get(), this._captureMemorySetting.get(), this._captureLayersAndPicturesSetting && this._captureLayersAndPicturesSetting.get());
if (WebInspector.experimentsSettings.timelineNoLiveUpdate.isEnabled() && this._lazyFrameModel)
this._lazyFrameModel.setMergeRecords(false);
......
......@@ -124,20 +124,31 @@ WebInspector.TimelineTracingView.prototype = {
{
WebInspector.Revealer.reveal(new WebInspector.DeferredTracingLayerTree(this._tracingModel.target(), record.args["snapshot"]["active_tree"]["root_layer"]));
}
if (record.name === "cc::LayerTreeHostImpl") {
/**
* @param {!Node=} node
* @this {WebInspector.TimelineTracingView}
*/
function appendPreviewAndshowDetails(node)
{
if (node)
contentHelper.appendElementRow("Preview", node);
this._delegate.showInDetails(WebInspector.UIString("Selected Event"), contentHelper.element);
}
var recordTypes = WebInspector.TracingTimelineModel.RecordType;
switch (record.name) {
case recordTypes.PictureSnapshot:
WebInspector.TracingTimelineUIUtils._buildPicturePreviewContent(record.args["snapshot"]["skp64"], appendPreviewAndshowDetails.bind(this));
break;
case recordTypes.LayerTreeHostImplSnapshot:
var link = document.createElement("span");
link.classList.add("revealable-link");
link.textContent = "show";
link.addEventListener("click", reveal.bind(this), false);
contentHelper.appendElementRow(WebInspector.UIString("Layer tree"), link);
} else if (record.name === "cc::Picture") {
var div = document.createElement("div");
div.className = "image-preview-container";
var img = div.createChild("img");
contentHelper.appendElementRow("Preview", div);
this._requestThumbnail(img, record.args["snapshot"]["skp64"]);
// Fall-through intended.
default:
this._delegate.showInDetails(WebInspector.UIString("Selected Event"), contentHelper.element);
}
this._delegate.showInDetails(WebInspector.UIString("Selected Event"), contentHelper.element);
},
/**
......@@ -163,43 +174,6 @@ WebInspector.TimelineTracingView.prototype = {
return table;
},
/**
* @param {!Element} img
* @param {string} encodedPicture
*/
_requestThumbnail: function(img, encodedPicture)
{
var snapshotId;
LayerTreeAgent.loadSnapshot(encodedPicture, onSnapshotLoaded);
/**
* @param {string} error
* @param {string} id
*/
function onSnapshotLoaded(error, id)
{
if (error) {
console.error("LayerTreeAgent.loadSnapshot(): " + error);
return;
}
snapshotId = id;
LayerTreeAgent.replaySnapshot(snapshotId, onSnapshotReplayed);
}
/**
* @param {string} error
* @param {string} encodedBitmap
*/
function onSnapshotReplayed(error, encodedBitmap)
{
LayerTreeAgent.releaseSnapshot(snapshotId);
if (error) {
console.error("LayerTreeAgent.replaySnapshot(): " + error);
return;
}
img.src = encodedBitmap;
}
},
__proto__: WebInspector.VBox.prototype
};
......
......@@ -32,6 +32,7 @@ WebInspector.TracingTimelineModel.RecordType = {
RecalculateStyles: "RecalculateStyles",
InvalidateLayout: "InvalidateLayout",
Layout: "Layout",
UpdateLayer: "UpdateLayer",
PaintSetup: "PaintSetup",
Paint: "Paint",
PaintImage: "PaintImage",
......@@ -89,7 +90,8 @@ WebInspector.TracingTimelineModel.RecordType = {
DecodeLazyPixelRef: "Decode LazyPixelRef",
LazyPixelRef: "LazyPixelRef",
LayerTreeHostImplSnapshot: "cc::LayerTreeHostImpl"
LayerTreeHostImplSnapshot: "cc::LayerTreeHostImpl",
PictureSnapshot: "cc::Picture"
};
WebInspector.TracingTimelineModel.defaultTracingCategoryFilter = "*,disabled-by-default-cc.debug,disabled-by-default-devtools.timeline,disabled-by-default-devtools.timeline.frame";
......@@ -98,8 +100,9 @@ WebInspector.TracingTimelineModel.prototype = {
/**
* @param {boolean} captureStacks
* @param {boolean} captureMemory
* @param {boolean} capturePictures
*/
startRecording: function(captureStacks, captureMemory)
startRecording: function(captureStacks, captureMemory, capturePictures)
{
var categories;
if (WebInspector.experimentsSettings.timelineTracingMode.isEnabled()) {
......@@ -108,6 +111,8 @@ WebInspector.TracingTimelineModel.prototype = {
var categoriesArray = ["disabled-by-default-devtools.timeline", "disabled-by-default-devtools.timeline.frame", "devtools"];
if (captureStacks)
categoriesArray.push("disabled-by-default-devtools.timeline.stack");
if (capturePictures)
categoriesArray.push("disabled-by-default-devtools.timeline.layers", "disabled-by-default-devtools.timeline.picture");
categories = categoriesArray.join(",");
}
this._startRecordingWithCategories(categories);
......@@ -254,7 +259,7 @@ WebInspector.TracingTimelineModel.prototype = {
this._lastScheduleStyleRecalculation = {};
this._webSocketCreateEvents = {};
this._paintImageEventByPixelRefId = {};
this._lastPaintForLayer = {};
this._lastRecalculateStylesEvent = null;
this._currentScriptEvent = null;
this._eventStack = [];
......@@ -394,7 +399,23 @@ WebInspector.TracingTimelineModel.prototype = {
case recordTypes.Paint:
event.highlightQuad = event.args["data"]["clip"];
// Initionally fall through.
event.backendNodeId = event.args["data"]["nodeId"];
var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== this._inspectedTargetLayerTreeId)
break;
this._lastPaintForLayer[layerUpdateEvent.args["layerId"]] = event;
break;
case recordTypes.PictureSnapshot:
var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== this._inspectedTargetLayerTreeId)
break;
var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["layerId"]];
if (!paintEvent)
break;
paintEvent.picture = event.args["snapshot"]["skp64"];
break;
case recordTypes.ScrollLayer:
event.backendNodeId = event.args["data"]["nodeId"];
break;
......
......@@ -40,6 +40,7 @@ WebInspector.TracingTimelineUIUtils._initEventStyles = function()
eventStyles[recordTypes.InvalidateLayout] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Invalidate Layout"), categories["rendering"]);
eventStyles[recordTypes.Layout] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Layout"), categories["rendering"]);
eventStyles[recordTypes.PaintSetup] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint Setup"), categories["painting"]);
eventStyles[recordTypes.UpdateLayer] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Update Layer"), categories["painting"]);
eventStyles[recordTypes.Paint] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint"), categories["painting"]);
eventStyles[recordTypes.Rasterize] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint"), categories["painting"]);
eventStyles[recordTypes.RasterTask] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint"), categories["painting"]);
......@@ -265,8 +266,12 @@ WebInspector.TracingTimelineUIUtils.buildTraceEventDetails = function(event, mod
{
var relatedNode = null;
var barrier = new CallbackBarrier();
if (event.imageURL && !event.previewElement)
WebInspector.DOMPresentationUtils.buildImagePreviewContents(target, event.imageURL, false, barrier.createCallback(saveImage));
if (!event.previewElement) {
if (event.imageURL)
WebInspector.DOMPresentationUtils.buildImagePreviewContents(target, event.imageURL, false, barrier.createCallback(saveImage));
else if (event.picture)
WebInspector.TracingTimelineUIUtils._buildPicturePreviewContent(event.picture, barrier.createCallback(saveImage));
}
if (event.backendNodeId)
target.domModel.pushNodesByBackendIdsToFrontend([event.backendNodeId], barrier.createCallback(setRelatedNode));
barrier.callWhenDone(callbackWrapper);
......@@ -357,8 +362,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
var url = (event.name === recordTypes.ResourceSendRequest) ? eventData["url"] : initiator.args.data["url"];
if (url)
contentHelper.appendElementRow(WebInspector.UIString("Resource"), WebInspector.linkifyResourceAsNode(url));
if (event.previewElement)
contentHelper.appendElementRow(WebInspector.UIString("Preview"), event.previewElement);
if (eventData["requestMethod"])
contentHelper.appendTextRow(WebInspector.UIString("Request Method"), eventData["requestMethod"]);
if (typeof eventData["statusCode"] === "number")
......@@ -394,8 +397,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
relatedNodeLabel = WebInspector.UIString("Image element");
if (event.imageURL)
contentHelper.appendElementRow(WebInspector.UIString("Image URL"), WebInspector.linkifyResourceAsNode(event.imageURL));
if (event.previewElement)
contentHelper.appendElementRow(WebInspector.UIString("Preview"), event.previewElement);
break;
case recordTypes.RecalculateStyles: // We don't want to see default details.
contentHelper.appendTextRow(WebInspector.UIString("Elements affected"), event.args["elementCount"]);
......@@ -457,6 +458,8 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
div.textContent = warning;
contentHelper.appendElementRow(WebInspector.UIString("Warning"), div);
}
if (event.previewElement)
contentHelper.appendElementRow(WebInspector.UIString("Preview"), event.previewElement);
fragment.appendChild(contentHelper.element);
return fragment;
}
......@@ -497,3 +500,47 @@ WebInspector.TracingTimelineUIUtils._aggregatedStatsForTraceEvent = function(mod
}
return { aggregatedStats: aggregatedStats, hasChildren: hasChildren };
}
/**
* @param {string} encodedPicture
* @param {function(!Element=)} callback
*/
WebInspector.TracingTimelineUIUtils._buildPicturePreviewContent = function(encodedPicture, callback)
{
var snapshotId;
LayerTreeAgent.loadSnapshot(encodedPicture, onSnapshotLoaded);
/**
* @param {string} error
* @param {string} id
*/
function onSnapshotLoaded(error, id)
{
if (error) {
console.error("LayerTreeAgent.loadSnapshot(): " + error);
callback();
return;
}
snapshotId = id;
LayerTreeAgent.replaySnapshot(snapshotId, onSnapshotReplayed);
}
/**
* @param {string} error
* @param {string} encodedBitmap
*/
function onSnapshotReplayed(error, encodedBitmap)
{
LayerTreeAgent.releaseSnapshot(snapshotId);
if (error) {
console.error("LayerTreeAgent.replaySnapshot(): " + error);
callback();
return;
}
var container = document.createElement("div");
container.className = "image-preview-container";
var img = container.createChild("img");
img.src = encodedBitmap;
callback(container);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment