1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 tvcm.require('tvcm.guid');
8 tvcm.require('tvcm.rect');
9 tvcm.require('tvcm.raf');
10 tvcm.require('tracing.trace_model.object_instance');
11 tvcm.require('cc.picture_as_image_data');
12 tvcm.require('cc.util');
14 tvcm.exportTo('cc', function() {
16 var ObjectSnapshot = tracing.trace_model.ObjectSnapshot;
18 // Number of pictures created. Used as an uniqueId because we are immutable.
20 var OPS_TIMING_ITERATIONS = 3;
22 function Picture(skp64, layerRect, opaqueRect) {
24 this.layerRect_ = layerRect;
25 this.opaqueRect_ = opaqueRect;
27 this.guid_ = tvcm.GUID.allocate();
36 return this.layerRect_;
43 getBase64SkpData: function() {
48 if (!PictureSnapshot.CanGetOps()) {
49 console.error(PictureSnapshot.HowToEnablePictureDebugging());
53 var ops = window.chrome.skiaBenchmarking.getOps({
56 layer_rect: this.layerRect_.toArray(),
57 opaque_rect: this.opaqueRect_.toArray()
62 console.error('Failed to get picture ops.');
67 getOpTimings: function() {
68 if (!PictureSnapshot.CanGetOpTimings()) {
69 console.error(PictureSnapshot.HowToEnablePictureDebugging());
73 var opTimings = window.chrome.skiaBenchmarking.getOpTimings({
76 layer_rect: this.layerRect_.toArray(),
77 opaque_rect: this.opaqueRect_.toArray()
82 console.error('Failed to get picture op timings.');
88 * Tag each op with the time it takes to rasterize.
90 * FIXME: We should use real statistics to get better numbers here, see
91 * https://code.google.com/p/trace-viewer/issues/detail?id=357
93 * @param {Array} ops Array of Skia operations.
94 * @return {Array} Skia ops where op.cmd_time contains the associated time
97 tagOpsWithTimings: function(ops) {
98 var opTimings = new Array();
99 for (var iteration = 0; iteration < OPS_TIMING_ITERATIONS; iteration++) {
100 opTimings[iteration] = this.getOpTimings();
101 if (!opTimings[iteration] || !opTimings[iteration].cmd_times)
103 if (opTimings[iteration].cmd_times.length != ops.length)
107 for (var opIndex = 0; opIndex < ops.length; opIndex++) {
108 var min = Number.MAX_VALUE;
109 for (var i = 0; i < OPS_TIMING_ITERATIONS; i++)
110 min = Math.min(min, opTimings[i].cmd_times[opIndex]);
111 ops[opIndex].cmd_time = min;
118 * Rasterize the picture.
120 * @param {{opt_stopIndex: number, params}} The SkPicture operation to
121 * rasterize up to. If not defined, the entire SkPicture is rasterized.
122 * @param {{opt_showOverdraw: bool, params}} Defines whether pixel overdraw
123 should be visualized in the image.
124 * @param {function(cc.PictureAsImageData)} The callback function that is
125 * called after rasterization is complete or fails.
127 rasterize: function(params, rasterCompleteCallback) {
128 if (!PictureSnapshot.CanRasterize() || !PictureSnapshot.CanGetOps()) {
129 rasterCompleteCallback(new cc.PictureAsImageData(
130 this, cc.PictureSnapshot.HowToEnablePictureDebugging()));
134 var raster = window.chrome.skiaBenchmarking.rasterize(
138 layer_rect: this.layerRect_.toArray(),
139 opaque_rect: this.opaqueRect_.toArray()
143 stop: params.stopIndex === undefined ? -1 : params.stopIndex,
144 overdraw: !!params.showOverdraw,
149 var canvas = document.createElement('canvas');
150 var ctx = canvas.getContext('2d');
151 canvas.width = raster.width;
152 canvas.height = raster.height;
153 var imageData = ctx.createImageData(raster.width, raster.height);
154 imageData.data.set(new Uint8ClampedArray(raster.data));
155 rasterCompleteCallback(new cc.PictureAsImageData(this, imageData));
157 var error = 'Failed to rasterize picture. ' +
158 'Your recording may be from an old Chrome version. ' +
159 'The SkPicture format is not backward compatible.';
160 rasterCompleteCallback(new cc.PictureAsImageData(this, error));
165 function LayeredPicture(pictures) {
166 this.guid_ = tvcm.GUID.allocate();
167 this.pictures_ = pictures;
168 this.layerRect_ = undefined;
171 LayeredPicture.prototype = {
172 __proto__: Picture.prototype,
179 return 'cc::LayeredPicture';
183 if (this.layerRect_ !== undefined)
184 return this.layerRect_;
193 for (var i = 0; i < this.pictures_.length; ++i) {
194 var rect = this.pictures_[i].layerRect;
195 this.layerRect_.x = Math.min(this.layerRect_.x, rect.x);
196 this.layerRect_.y = Math.min(this.layerRect_.y, rect.y);
197 this.layerRect_.width =
198 Math.max(this.layerRect_.width, rect.x + rect.width);
199 this.layerRect_.height =
200 Math.max(this.layerRect_.height, rect.y + rect.height);
202 return this.layerRect_;
209 getBase64SkpData: function() {
210 throw new Error('Not available with a LayeredPicture.');
215 for (var i = 0; i < this.pictures_.length; ++i)
216 ops = ops.concat(this.pictures_[i].getOps());
220 getOpTimings: function() {
221 var opTimings = this.pictures_[0].getOpTimings();
222 for (var i = 1; i < this.pictures_.length; ++i) {
223 var timings = this.pictures_[i].getOpTimings();
224 opTimings.cmd_times = opTimings.cmd_times.concat(timings.cmd_times);
225 opTimings.total_time += timings.total_time;
230 tagOpsWithTimings: function(ops) {
231 var opTimings = new Array();
232 for (var iteration = 0; iteration < OPS_TIMING_ITERATIONS; iteration++) {
233 opTimings[iteration] = this.getOpTimings();
234 if (!opTimings[iteration] || !opTimings[iteration].cmd_times)
238 for (var opIndex = 0; opIndex < ops.length; opIndex++) {
239 var min = Number.MAX_VALUE;
240 for (var i = 0; i < OPS_TIMING_ITERATIONS; i++)
241 min = Math.min(min, opTimings[i].cmd_times[opIndex]);
242 ops[opIndex].cmd_time = min;
247 rasterize: function(params, rasterCompleteCallback) {
248 this.picturesAsImageData_ = [];
249 var rasterCallback = function(pictureAsImageData) {
250 this.picturesAsImageData_.push(pictureAsImageData);
251 if (this.picturesAsImageData_.length !== this.pictures_.length)
254 var canvas = document.createElement('canvas');
255 var ctx = canvas.getContext('2d');
256 canvas.width = this.layerRect.width;
257 canvas.height = this.layerRect.height;
259 // TODO(dsinclair): Verify these finish in the order started.
260 // Do the rasterize calls run sync or asyn? As the imageData
261 // going to be in the same order as the pictures_ list?
262 for (var i = 0; i < this.picturesAsImageData_.length; ++i) {
263 ctx.putImageData(this.picturesAsImageData_[i].imageData,
264 this.pictures_[i].layerRect.x,
265 this.pictures_[i].layerRect.y);
267 this.picturesAsImageData_ = [];
269 rasterCompleteCallback(new cc.PictureAsImageData(this,
270 ctx.getImageData(this.layerRect.x, this.layerRect.y,
271 this.layerRect.width, this.layerRect.height)));
274 for (var i = 0; i < this.pictures_.length; ++i)
275 this.pictures_[i].rasterize(params, rasterCallback);
283 function PictureSnapshot() {
284 ObjectSnapshot.apply(this, arguments);
287 PictureSnapshot.HasSkiaBenchmarking = function() {
290 if (!window.chrome.skiaBenchmarking)
295 PictureSnapshot.CanRasterize = function() {
296 if (!PictureSnapshot.HasSkiaBenchmarking())
298 if (!window.chrome.skiaBenchmarking.rasterize)
303 PictureSnapshot.CanGetOps = function() {
304 if (!PictureSnapshot.HasSkiaBenchmarking())
306 if (!window.chrome.skiaBenchmarking.getOps)
311 PictureSnapshot.CanGetOpTimings = function() {
312 if (!PictureSnapshot.HasSkiaBenchmarking())
314 if (!window.chrome.skiaBenchmarking.getOpTimings)
319 PictureSnapshot.CanGetInfo = function() {
320 if (!PictureSnapshot.HasSkiaBenchmarking())
322 if (!window.chrome.skiaBenchmarking.getInfo)
327 PictureSnapshot.HowToEnablePictureDebugging = function() {
329 'For pictures to show up, you need to have Chrome running with ',
330 '--enable-skia-benchmarking. Please restart chrome with this flag ',
336 if (!window.chrome.skiaBenchmarking)
338 if (!window.chrome.skiaBenchmarking.rasterize)
339 return 'Your chrome is old';
340 if (!window.chrome.skiaBenchmarking.getOps)
341 return 'Your chrome is old: skiaBenchmarking.getOps not found';
342 if (!window.chrome.skiaBenchmarking.getOpTimings)
343 return 'Your chrome is old: skiaBenchmarking.getOpTimings not found';
344 if (!window.chrome.skiaBenchmarking.getInfo)
345 return 'Your chrome is old: skiaBenchmarking.getInfo not found';
346 return 'Rasterizing is on';
349 PictureSnapshot.prototype = {
350 __proto__: ObjectSnapshot.prototype,
352 preInitialize: function() {
353 cc.preInitializeObject(this);
354 this.rasterResult_ = undefined;
357 initialize: function() {
358 // If we have an alias args, that means this picture was represented
359 // by an alias, and the real args is in alias.args.
361 this.args = this.args.alias.args;
363 if (!this.args.params.layerRect)
364 throw new Error('Missing layer rect');
366 this.layerRect_ = this.args.params.layerRect;
367 this.picture_ = new Picture(this.args.skp64,
368 this.args.params.layerRect, this.args.params.opaqueRect);
371 set picture(picture) {
372 this.picture_ = picture;
376 return this.picture_.canSave;
380 return this.layerRect_ ? this.layerRect_ : this.picture_.layerRect;
384 return this.picture_.guid;
387 getBase64SkpData: function() {
388 return this.picture_.getBase64SkpData();
392 return this.picture_.getOps();
395 getOpTimings: function() {
396 return this.picture_.getOpTimings();
399 tagOpsWithTimings: function(ops) {
400 return this.picture_.tagOpsWithTimings(ops);
403 rasterize: function(params, rasterCompleteCallback) {
404 this.picture_.rasterize(params, rasterCompleteCallback);
408 ObjectSnapshot.register('cc::Picture', PictureSnapshot);
411 PictureSnapshot: PictureSnapshot,
413 LayeredPicture: LayeredPicture