blob: 0939216d3f3677c65b3b8327e57be72a96159d21 [file] [log] [blame]
Chris Craik93216d02015-03-05 13:58:42 -08001<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
Chris Craik24385db2015-06-11 11:16:26 -07008<link rel="import" href="/extras/chrome/cc/picture_as_image_data.html">
9<link rel="import" href="/extras/chrome/cc/util.html">
Chris Craik93216d02015-03-05 13:58:42 -080010<link rel="import" href="/base/guid.html">
11<link rel="import" href="/base/rect.html">
12<link rel="import" href="/base/raf.html">
Chris Craik24385db2015-06-11 11:16:26 -070013<link rel="import" href="/model/object_instance.html">
Chris Craik93216d02015-03-05 13:58:42 -080014
15<script>
16'use strict';
17
Chris Craik24385db2015-06-11 11:16:26 -070018tr.exportTo('tr.e.cc', function() {
19 var ObjectSnapshot = tr.model.ObjectSnapshot;
Chris Craik93216d02015-03-05 13:58:42 -080020
21 // Number of pictures created. Used as an uniqueId because we are immutable.
22 var PictureCount = 0;
23 var OPS_TIMING_ITERATIONS = 3;
24
25 function Picture(skp64, layerRect) {
26 this.skp64_ = skp64;
27 this.layerRect_ = layerRect;
28
Chris Craik24385db2015-06-11 11:16:26 -070029 this.guid_ = tr.b.GUID.allocate();
Chris Craik93216d02015-03-05 13:58:42 -080030 }
31
32 Picture.prototype = {
33 get canSave() {
34 return true;
35 },
36
37 get layerRect() {
38 return this.layerRect_;
39 },
40
41 get guid() {
42 return this.guid_;
43 },
44
45 getBase64SkpData: function() {
46 return this.skp64_;
47 },
48
49 getOps: function() {
50 if (!PictureSnapshot.CanGetOps()) {
51 console.error(PictureSnapshot.HowToEnablePictureDebugging());
52 return undefined;
53 }
54
55 var ops = window.chrome.skiaBenchmarking.getOps({
56 skp64: this.skp64_,
57 params: {
58 layer_rect: this.layerRect_.toArray()
59 }
60 });
61
62 if (!ops)
63 console.error('Failed to get picture ops.');
64
65 return ops;
66 },
67
68 getOpTimings: function() {
69 if (!PictureSnapshot.CanGetOpTimings()) {
70 console.error(PictureSnapshot.HowToEnablePictureDebugging());
71 return undefined;
72 }
73
74 var opTimings = window.chrome.skiaBenchmarking.getOpTimings({
75 skp64: this.skp64_,
76 params: {
77 layer_rect: this.layerRect_.toArray()
78 }
79 });
80
81 if (!opTimings)
82 console.error('Failed to get picture op timings.');
83
84 return opTimings;
85 },
86
87 /**
88 * Tag each op with the time it takes to rasterize.
89 *
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
92 *
93 * @param {Array} ops Array of Skia operations.
94 * @return {Array} Skia ops where op.cmd_time contains the associated time
95 * for a given op.
96 */
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)
102 return ops;
103 if (opTimings[iteration].cmd_times.length != ops.length)
104 return ops;
105 }
106
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;
112 }
113
114 return ops;
115 },
116
117 /**
118 * Rasterize the picture.
119 *
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.
Chris Craik24385db2015-06-11 11:16:26 -0700124 * @param {function(tr.e.cc.PictureAsImageData)} The callback function that
Chris Craik93216d02015-03-05 13:58:42 -0800125 * is called after rasterization is complete or fails.
126 */
127 rasterize: function(params, rasterCompleteCallback) {
128 if (!PictureSnapshot.CanRasterize() || !PictureSnapshot.CanGetOps()) {
Chris Craik24385db2015-06-11 11:16:26 -0700129 rasterCompleteCallback(new tr.e.cc.PictureAsImageData(
130 this, tr.e.cc.PictureSnapshot.HowToEnablePictureDebugging()));
Chris Craik93216d02015-03-05 13:58:42 -0800131 return;
132 }
133
134 var raster = window.chrome.skiaBenchmarking.rasterize(
135 {
136 skp64: this.skp64_,
137 params: {
138 layer_rect: this.layerRect_.toArray()
139 }
140 },
141 {
142 stop: params.stopIndex === undefined ? -1 : params.stopIndex,
143 overdraw: !!params.showOverdraw,
144 params: { }
145 });
146
147 if (raster) {
148 var canvas = document.createElement('canvas');
149 var ctx = canvas.getContext('2d');
150 canvas.width = raster.width;
151 canvas.height = raster.height;
152 var imageData = ctx.createImageData(raster.width, raster.height);
153 imageData.data.set(new Uint8ClampedArray(raster.data));
Chris Craik24385db2015-06-11 11:16:26 -0700154 rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this, imageData));
Chris Craik93216d02015-03-05 13:58:42 -0800155 } else {
156 var error = 'Failed to rasterize picture. ' +
157 'Your recording may be from an old Chrome version. ' +
158 'The SkPicture format is not backward compatible.';
Chris Craik24385db2015-06-11 11:16:26 -0700159 rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this, error));
Chris Craik93216d02015-03-05 13:58:42 -0800160 }
161 }
162 };
163
164 function LayeredPicture(pictures) {
Chris Craik24385db2015-06-11 11:16:26 -0700165 this.guid_ = tr.b.GUID.allocate();
Chris Craik93216d02015-03-05 13:58:42 -0800166 this.pictures_ = pictures;
167 this.layerRect_ = undefined;
168 }
169
170 LayeredPicture.prototype = {
171 __proto__: Picture.prototype,
172
173 get canSave() {
174 return false;
175 },
176
177 get typeName() {
178 return 'cc::LayeredPicture';
179 },
180
181 get layerRect() {
182 if (this.layerRect_ !== undefined)
183 return this.layerRect_;
184
185 this.layerRect_ = {
186 x: 0,
187 y: 0,
188 width: 0,
189 height: 0
190 };
191
192 for (var i = 0; i < this.pictures_.length; ++i) {
193 var rect = this.pictures_[i].layerRect;
194 this.layerRect_.x = Math.min(this.layerRect_.x, rect.x);
195 this.layerRect_.y = Math.min(this.layerRect_.y, rect.y);
196 this.layerRect_.width =
197 Math.max(this.layerRect_.width, rect.x + rect.width);
198 this.layerRect_.height =
199 Math.max(this.layerRect_.height, rect.y + rect.height);
200 }
201 return this.layerRect_;
202 },
203
204 get guid() {
205 return this.guid_;
206 },
207
208 getBase64SkpData: function() {
209 throw new Error('Not available with a LayeredPicture.');
210 },
211
212 getOps: function() {
213 var ops = [];
214 for (var i = 0; i < this.pictures_.length; ++i)
215 ops = ops.concat(this.pictures_[i].getOps());
216 return ops;
217 },
218
219 getOpTimings: function() {
220 var opTimings = this.pictures_[0].getOpTimings();
221 for (var i = 1; i < this.pictures_.length; ++i) {
222 var timings = this.pictures_[i].getOpTimings();
223 opTimings.cmd_times = opTimings.cmd_times.concat(timings.cmd_times);
224 opTimings.total_time += timings.total_time;
225 }
226 return opTimings;
227 },
228
229 tagOpsWithTimings: function(ops) {
230 var opTimings = new Array();
231 for (var iteration = 0; iteration < OPS_TIMING_ITERATIONS; iteration++) {
232 opTimings[iteration] = this.getOpTimings();
233 if (!opTimings[iteration] || !opTimings[iteration].cmd_times)
234 return ops;
235 }
236
237 for (var opIndex = 0; opIndex < ops.length; opIndex++) {
238 var min = Number.MAX_VALUE;
239 for (var i = 0; i < OPS_TIMING_ITERATIONS; i++)
240 min = Math.min(min, opTimings[i].cmd_times[opIndex]);
241 ops[opIndex].cmd_time = min;
242 }
243 return ops;
244 },
245
246 rasterize: function(params, rasterCompleteCallback) {
247 this.picturesAsImageData_ = [];
248 var rasterCallback = function(pictureAsImageData) {
249 this.picturesAsImageData_.push(pictureAsImageData);
250 if (this.picturesAsImageData_.length !== this.pictures_.length)
251 return;
252
253 var canvas = document.createElement('canvas');
254 var ctx = canvas.getContext('2d');
255 canvas.width = this.layerRect.width;
256 canvas.height = this.layerRect.height;
257
258 // TODO(dsinclair): Verify these finish in the order started.
259 // Do the rasterize calls run sync or asyn? As the imageData
260 // going to be in the same order as the pictures_ list?
261 for (var i = 0; i < this.picturesAsImageData_.length; ++i) {
262 ctx.putImageData(this.picturesAsImageData_[i].imageData,
263 this.pictures_[i].layerRect.x,
264 this.pictures_[i].layerRect.y);
265 }
266 this.picturesAsImageData_ = [];
267
Chris Craik24385db2015-06-11 11:16:26 -0700268 rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,
Chris Craik93216d02015-03-05 13:58:42 -0800269 ctx.getImageData(this.layerRect.x, this.layerRect.y,
270 this.layerRect.width, this.layerRect.height)));
271 }.bind(this);
272
273 for (var i = 0; i < this.pictures_.length; ++i)
274 this.pictures_[i].rasterize(params, rasterCallback);
275 }
276 };
277
278
279 /**
280 * @constructor
281 */
282 function PictureSnapshot() {
283 ObjectSnapshot.apply(this, arguments);
284 }
285
286 PictureSnapshot.HasSkiaBenchmarking = function() {
287 if (!window.chrome)
288 return false;
289 if (!window.chrome.skiaBenchmarking)
290 return false;
291 return true;
292 }
293
294 PictureSnapshot.CanRasterize = function() {
295 if (!PictureSnapshot.HasSkiaBenchmarking())
296 return false;
297 if (!window.chrome.skiaBenchmarking.rasterize)
298 return false;
299 return true;
300 }
301
302 PictureSnapshot.CanGetOps = function() {
303 if (!PictureSnapshot.HasSkiaBenchmarking())
304 return false;
305 if (!window.chrome.skiaBenchmarking.getOps)
306 return false;
307 return true;
308 }
309
310 PictureSnapshot.CanGetOpTimings = function() {
311 if (!PictureSnapshot.HasSkiaBenchmarking())
312 return false;
313 if (!window.chrome.skiaBenchmarking.getOpTimings)
314 return false;
315 return true;
316 }
317
318 PictureSnapshot.CanGetInfo = function() {
319 if (!PictureSnapshot.HasSkiaBenchmarking())
320 return false;
321 if (!window.chrome.skiaBenchmarking.getInfo)
322 return false;
323 return true;
324 }
325
326 PictureSnapshot.HowToEnablePictureDebugging = function() {
327 var usualReason = [
328 'For pictures to show up, you need to have Chrome running with ',
329 '--enable-skia-benchmarking. Please restart chrome with this flag ',
330 'and try again.'
331 ].join('');
332
333 if (!window.chrome)
334 return usualReason;
335 if (!window.chrome.skiaBenchmarking)
336 return usualReason;
337 if (!window.chrome.skiaBenchmarking.rasterize)
338 return 'Your chrome is old';
339 if (!window.chrome.skiaBenchmarking.getOps)
340 return 'Your chrome is old: skiaBenchmarking.getOps not found';
341 if (!window.chrome.skiaBenchmarking.getOpTimings)
342 return 'Your chrome is old: skiaBenchmarking.getOpTimings not found';
343 if (!window.chrome.skiaBenchmarking.getInfo)
344 return 'Your chrome is old: skiaBenchmarking.getInfo not found';
345 return 'Rasterizing is on';
346 }
347
348 PictureSnapshot.prototype = {
349 __proto__: ObjectSnapshot.prototype,
350
351 preInitialize: function() {
Chris Craik24385db2015-06-11 11:16:26 -0700352 tr.e.cc.preInitializeObject(this);
Chris Craik93216d02015-03-05 13:58:42 -0800353 this.rasterResult_ = undefined;
354 },
355
356 initialize: function() {
357 // If we have an alias args, that means this picture was represented
358 // by an alias, and the real args is in alias.args.
359 if (this.args.alias)
360 this.args = this.args.alias.args;
361
362 if (!this.args.params.layerRect)
363 throw new Error('Missing layer rect');
364
365 this.layerRect_ = this.args.params.layerRect;
366 this.picture_ = new Picture(this.args.skp64, this.args.params.layerRect);
367 },
368
369 set picture(picture) {
370 this.picture_ = picture;
371 },
372
373 get canSave() {
374 return this.picture_.canSave;
375 },
376
377 get layerRect() {
378 return this.layerRect_ ? this.layerRect_ : this.picture_.layerRect;
379 },
380
381 get guid() {
382 return this.picture_.guid;
383 },
384
385 getBase64SkpData: function() {
386 return this.picture_.getBase64SkpData();
387 },
388
389 getOps: function() {
390 return this.picture_.getOps();
391 },
392
393 getOpTimings: function() {
394 return this.picture_.getOpTimings();
395 },
396
397 tagOpsWithTimings: function(ops) {
398 return this.picture_.tagOpsWithTimings(ops);
399 },
400
401 rasterize: function(params, rasterCompleteCallback) {
402 this.picture_.rasterize(params, rasterCompleteCallback);
403 }
404 };
405
406 ObjectSnapshot.register(
407 PictureSnapshot,
408 {typeNames: ['cc::Picture']});
409
410 return {
411 PictureSnapshot: PictureSnapshot,
412 Picture: Picture,
413 LayeredPicture: LayeredPicture
414 };
415});
416</script>