Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(483)

Side by Side Diff: third_party/WebKit/Source/modules/canvas2d/tools/GeneratePerformanceData.js

Issue 2903053002: Removing canvas rendering mode switching feature (Closed)
Patch Set: fix test failure Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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.
4
5 // ==========//
6 // Constants //
7 // ==========//
8
9 var CALL_TYPE = {
10 PATH : "PATH",
11 IMAGE : "IMAGE",
12 GET_DATA : "GET_DATA",
13 PUT_DATA : "PUT_DATA"
14 }
15
16 var SHAPE_TYPE = {
17 STROKE_PATH : "STROKE_PATH",
18 FILL_CONVEX_PATH : "FILL_CONVEX_PATH",
19 FILL_NON_CONVEX_PATH : "FILL_NON_CONVEX_PATH",
20 STROKE_RECT : "STROKE_RECT",
21 FILL_RECT : "FILL_RECT",
22 STROKE_TEXT : "STROKE_TEXT",
23 FILL_TEXT : "FILL_TEXT"
24 }
25
26 var SHAPE_STYLE = {
27 COLOR : "COLOR",
28 LINEAR_GRADIENT : "LINEAR_GRADIENT",
29 RADIAL_GRADIENT: "RADIAL_GRADIENT",
30 PATTERN : "PATTERN"
31 }
32
33 var IMAGE_TYPE = {
34 DRAW_SVG_IMAGE : "DRAW_SVG_IMAGE",
35 DRAW_PNG_IMAGE : "DRAW_PNG_IMAGE"
36 }
37
38 var NUMBER_OF_FRAME_PER_ESTIMATE = 3;
39 var NUMBER_OF_BLANK_FRAMES_BETWEEN_MEASUREMENTS = 5;
40
41 // The max frames per second (fps) should be less than 60 because the rendering
42 // speed should be the limiting factor, not screen refresh rate.
43 var MAX_FRAMES_PER_SECOND_FOR_MEASUREMENTS = 30;
44 var MIN_FRAMES_PER_SECOND_FOR_MEASUREMENTS = 10;
45 var TARGET_FPS = 20;
46 var TARGET_TPF = 1000 / TARGET_FPS;
47
48 // ================//
49 // State Variables //
50 // ================//
51
52 var recording_helper = new RecordingHelper();
53 recording_helper.restartRecordingTime();
54
55 // list of list of DrawParameter's to describe the each frame to draw
56 var list_of_frame_parameters = [];
57
58 // list of PerformanceMeasurement
59 var performance_measurements = [];
60
61 var frame_index = 0;
62 var blank_counter = 0;
63
64 // =============================//
65 // Main rendering loop function //
66 // =============================//
67
68 function drawAndRecord(context) {
69 if (frame_index >= list_of_frame_parameters.length) return;
70
71 if (blank_counter > 0) {
72 recording_helper.restartRecordingTime();
73 blank_counter--;
74 return;
75 }
76
77 var newMeasurement = false;
78 if (blank_counter < 0) {
79 if (frame_index == 0
80 && recording_helper.getNumberOfFramesRecorded() == 0) {
81 newMeasurement = true;
82 } else {
83 var tpf = recording_helper.getAverageTimePerFrame();
84 var fps = recording_helper.getAverageFramesPerSecond();
85
86 if (fps > MAX_FRAMES_PER_SECOND_FOR_MEASUREMENTS) {
87 // too fast, increase quantity and try again
88 var new_quantity = quantityForTarget(
89 list_of_frame_parameters[frame_index][0].quantity,
90 tpf, TARGET_TPF);
91
92 list_of_frame_parameters[frame_index][0].quantity = new_quantity;
93
94 // reset total bounding box area values
95 for (var i = 0; i<list_of_frame_parameters[frame_index].length; i++) {
96 list_of_frame_parameters[frame_index][i].resetBoundingBoxArea();
97 }
98
99 newMeasurement = true;
100
101 } else if (fps < MIN_FRAMES_PER_SECOND_FOR_MEASUREMENTS) {
102 // too slow, move on to the next measurement
103 console.log("Too slow - moving on to next measurement.");
104 frame_index++;
105 newMeasurement = true;
106 }
107
108 if (recording_helper.getNumberOfFramesRecorded() >=
109 NUMBER_OF_FRAME_PER_ESTIMATE && newMeasurement == false) {
110 if (fps < MAX_FRAMES_PER_SECOND_FOR_MEASUREMENTS) {
111 // Redering speed, not refresh rate is the limiting factor
112 console.log("Recording: frame_index = " +
113 frame_index + ",\n\ttfp = "+ tpf +
114 "\n\tfps = " + fps);
115
116 performance_measurements.push(
117 new PerformanceMeasurement(
118 tpf, fps,
119 list_of_frame_parameters[frame_index]));
120
121 }
122
123 frame_index++;
124 newMeasurement = true;
125 }
126 }
127
128 if (newMeasurement) {
129 recording_helper.restartRecordingTime();
130 blank_counter = NUMBER_OF_BLANK_FRAMES_BETWEEN_MEASUREMENTS;
131 if (frame_index >= list_of_frame_parameters.length) {
132 console.log(performance_measurements);
133 drawCSVString(getCSVString(performance_measurements));
134 return;
135 }
136 }
137 }
138
139 if (blank_counter == 0) {
140 recording_helper.restartRecordingTime();
141 blank_counter--;
142 }
143
144 DrawParameters(context, list_of_frame_parameters[frame_index]);
145 recording_helper.recordFrame();
146 }
147
148 // =====================//
149 // Data storage classes //
150 // =====================//
151
152 function DrawSize(width, height) {
153 this.width = Math.round(width);
154 this.height = Math.round(height);
155 }
156
157 function DrawParameter(
158 call_type, draw_sub_type, size, shadow_blur, quantity, fill_style) {
159 this.call_type = call_type; // path, image, put or get image data
160 this.draw_sub_type = draw_sub_type; // image type or path type
161 this.fill_style = fill_style; // for paths only
162 this.shadow_blur = shadow_blur;
163
164 // Target size of side of square bounding box. Actuall size can differ.
165 // total_bounding_box_area is returned by draw call and represents actuall
166 // area of the call.
167 this.size = size;
168
169 this.total_bounding_box_area = 0; // computed elsewhere
170 this.total_shadow_bounding_box_area = 0; // computed elsewhere
171 this.total_bounding_box_perimeter = 0; // computed elsewhere
172
173 this.resetBoundingBoxArea = function() {
174 this.total_bounding_box_area = 0;
175 this.total_shadow_bounding_box_area = 0;
176 this.total_bounding_box_perimeter = 0;
177 }
178
179 this.quantity = quantity;
180 }
181
182 function PerformanceMeasurement(tpf, fps, parameters) {
183 this.tpf = tpf; // time per frame
184 this.fps = fps; // frames per second
185 this.parameters = parameters; // DrawParameter object
186 }
187
188 // ===========================================================//
189 // Utility functions for generating lists of frame parameters //
190 // ===========================================================//
191
192 function generatePathFrameParameters() {
193 var path_sizes = [20, 50, 200, 300, 400];
194 var quantities = [10, 20, 50, 100, 500, 1000, 2000];
195 var shadow_blurs = [0, 1, 10, 20];
196
197 // Only draw combinations that contain shadows with this probability
198 // to reduce the number of permutations.
199 var shadowedPermutationProbability = 0.1;
200
201 var list_of_frame_parameters = [];
202
203 for (shape_type in SHAPE_TYPE) {
204 for (var size_i = 0; size_i < path_sizes.length; size_i++) {
205 for (var qty_i = 0; qty_i < quantities.length; qty_i++) {
206 for (var sh_i = 0; sh_i < shadow_blurs.length; sh_i++) {
207 for (fill_style in SHAPE_STYLE) {
208 if (shadow_blurs[sh_i] == 0
209 || getTrueWithProbability(shadowedPermutationProbability)) {
210
211 list_of_frame_parameters.push([
212 new DrawParameter(CALL_TYPE.PATH, shape_type,
213 path_sizes[size_i], shadow_blurs[sh_i],
214 quantities[qty_i], fill_style)]);
215 }
216 }
217 }
218 }
219 }
220 }
221 return list_of_frame_parameters;
222 }
223
224 function generateImageFrameParameters() {
225 var png_image_sizes = [5, 10, 20, 50, 90];
226 var svg_image_sizes = [5, 20, 50, 90, 200, 300];
227
228 var png_quantities = [10, 50, 100, 500, 1000];
229 var svg_quantities = [10, 50, 100, 200, 500];
230 var shadow_blurs = [0, 1, 5, 10];
231
232 // Only draw combinations that contain shadows with this probability
233 // to reduce the number of permutations.
234 var shadowedPermutationProbability = 0.1;
235
236 var list_of_frame_parameters = [];
237
238 for (var size_i = 0; size_i < png_image_sizes.length; size_i++) {
239 for (var qty_i = 0; qty_i < png_quantities.length; qty_i++) {
240 for (var sh_i = 0; sh_i < shadow_blurs.length; sh_i++) {
241 if (shadow_blurs[sh_i] == 0
242 || getTrueWithProbability(shadowedPermutationProbability)) {
243
244 list_of_frame_parameters.push([
245 new DrawParameter(CALL_TYPE.IMAGE,
246 IMAGE_TYPE.DRAW_PNG_IMAGE,
247 png_image_sizes[size_i],
248 shadow_blurs[sh_i],
249 png_quantities[qty_i], "")]);
250 }
251 }
252 }
253 }
254
255 for (var size_i = 0; size_i < svg_image_sizes.length; size_i++) {
256 for (var qty_i = 0; qty_i < svg_quantities.length; qty_i++) {
257 for (var sh_i = 0; sh_i < shadow_blurs.length; sh_i++) {
258 if (shadow_blurs[sh_i] == 0
259 || getTrueWithProbability(shadowedPermutationProbability)) {
260
261 list_of_frame_parameters.push([
262 new DrawParameter(CALL_TYPE.IMAGE,
263 IMAGE_TYPE.DRAW_SVG_IMAGE,
264 svg_image_sizes[size_i],
265 shadow_blurs[sh_i],
266 svg_quantities[qty_i], "")]);
267 }
268 }
269 }
270 }
271 return list_of_frame_parameters;
272 }
273
274 function generatePutDataFrameParameters(context) {
275 var sizes = [20, 50, 100, 200, 300];
276 var quantities = [10, 50, 100, 500, 1000, 2000];
277
278 var list_of_frame_parameters = [];
279
280 for (var size_i = 0; size_i < sizes.length; size_i++) {
281 for (var qty_i = 0; qty_i < quantities.length; qty_i++) {
282 // Call putImageData so it aquires and caches image data of
283 // the correct dimensions
284 putImageData(context, sizes[size_i]);
285
286 list_of_frame_parameters.push([
287 new DrawParameter(CALL_TYPE.PUT_DATA,
288 "",
289 sizes[size_i],
290 0,
291 quantities[qty_i], "")]);
292 }
293 }
294
295 return list_of_frame_parameters;
296 }
297
298 function generateGetDataFrameParameters(context) {
299 var sizes = [20, 50, 100, 200, 300];
300 var quantities = [10, 50, 100, 500, 1000, 2000];
301
302 var list_of_frame_parameters = [];
303
304 for (var size_i = 0; size_i < sizes.length; size_i++) {
305 for (var qty_i = 0; qty_i < quantities.length; qty_i++) {
306 list_of_frame_parameters.push([
307 new DrawParameter(CALL_TYPE.GET_DATA,
308 "",
309 sizes[size_i],
310 0,
311 quantities[qty_i], "")]);
312 }
313 }
314
315 return list_of_frame_parameters;
316 }
317
318 // Draw each DrawParameter in the parameter_list on the canvas context,
319 // updates their total_bounding_box_area, total_bounding_box_perimeter
320 // and total_shadow_bounding_box_area fields.
321 function DrawParameters(context, parameter_list) {
322 for (var i = 0; i < parameter_list.length; i++) {
323 var p = parameter_list[i];
324 p.resetBoundingBoxArea();
325 for (var count = 0; count < p.quantity; count++) {
326 var draw_size;
327 switch (p.call_type) {
328 case CALL_TYPE.PATH:
329 setShapeStyle(context, p.fill_style);
330 setShadeStyle(context, p.shadow_blur);
331 draw_size = drawShape(context, p.draw_sub_type, p.size);
332 break;
333 case CALL_TYPE.IMAGE:
334 setShadeStyle(context, p.shadow_blur);
335 draw_size = drawImageType(context, p.draw_sub_type, p.size);
336 break;
337 case CALL_TYPE.GET_DATA:
338 draw_size = getImageData(context, p.size);
339 break;
340 case CALL_TYPE.PUT_DATA:
341 draw_size = putImageData(context, p.size);
342 break;
343 default:
344 draw_size = new DrawSize(0, 0);
345 }
346
347 p.total_bounding_box_area +=
348 draw_size.width * draw_size.height;
349
350 p.total_bounding_box_perimeter +=
351 (2*draw_size.width) + (2*draw_size.height);
352
353 if (p.call_type == CALL_TYPE.PATH || p.call_type == CALL_TYPE.IMAGE) {
354 p.total_shadow_bounding_box_area +=
355 (draw_size.width+2*p.shadow_blur) *
356 (draw_size.height+2*p.shadow_blur);
357 }
358 }
359 }
360 }
361
362 // =======================================================================//
363 // Utility functions to make a CSV string that encodes the generated data //
364 // =======================================================================//
365
366 function getDataFrame(performance_measurements) {
367 var data = {};
368
369 // shadow_blur * total bounding box area
370 data["SHADOW_BLUR_TIMES_AREA"] = [];
371
372 // shadow_blur^2 * total bounding box area
373 data["SHADOW_BLUR_SQUARED_TIMES_AREA"] = [];
374
375 // shadow_blur^2 * total shadow bounding box area
376 data["SHADOW_BLUR_SQUARED_TIMES_SHADOW_AREA"] = [];
377
378 // the number of draw calls that have a shadow
379 data["NUM_SHADOWS"] = [];
380
381 data["CALL_TYPE"] = [];
382
383 for (var i = 0; i < performance_measurements.length; i++) {
384 var performance_measurement = performance_measurements[i];
385 for (var j = 0; j < performance_measurement.parameters.length; j++) {
386 var p = performance_measurement.parameters[j];
387 var call_type = p.call_type + "_" + p.draw_sub_type;
388 var call_type_area = p.call_type + "_" + p.draw_sub_type + "_AREA";
389
390 data[call_type] = [];
391 data[call_type_area] = [];
392
393 if (p.call_type == CALL_TYPE.PATH) {
394 data[p.fill_style] = [];
395 data[p.fill_style + "_AREA"] = [];
396 }
397 }
398 }
399
400 for (var i = 0; i < performance_measurements.length; i++) {
401 var performance_measurement = performance_measurements[i];
402 for (var j = 0; j < performance_measurement.parameters.length; j++) {
403 var p = performance_measurement.parameters[j];
404 for (var key in data) {
405 data[key].push(0);
406 }
407
408 var call_type = p.call_type + "_" + p.draw_sub_type;
409 var call_type_area = p.call_type + "_" + p.draw_sub_type + "_AREA";
410
411 data[call_type][i] += p.quantity;
412
413 data[call_type_area][i] += p.total_bounding_box_area;
414
415 if (p.call_type == CALL_TYPE.PATH) {
416 data[p.fill_style][i] += p.quantity;
417 data[p.fill_style + "_AREA"][i] += p.total_bounding_box_area;
418 }
419
420 if (p.call_type == CALL_TYPE.PATH || p.call_type == CALL_TYPE.IMAGE) {
421 data["NUM_SHADOWS"][i] += (p.shadow_blur > 0.0) ? p.quantity : 0;
422
423 data["SHADOW_BLUR_TIMES_AREA"][i] +=
424 p.total_bounding_box_area * p.shadow_blur;
425
426 data["SHADOW_BLUR_SQUARED_TIMES_AREA"][i] +=
427 p.total_bounding_box_area * p.shadow_blur * p.shadow_blur;
428
429 data["SHADOW_BLUR_SQUARED_TIMES_SHADOW_AREA"][i] +=
430 p.total_shadow_bounding_box_area * p.shadow_blur * p.shadow_blur;
431 }
432
433 data["CALL_TYPE"][i] = p.call_type;
434 }
435 }
436
437 return data;
438 }
439
440 function getCSVString(performance_measurements) {
441 var csv_string = "";
442
443 var data_frame = getDataFrame(performance_measurements);
444
445 csv_string += "TIME_PER_FRAME, FRAMES_PER_SECOND"
446 for (var key in data_frame) {
447 csv_string += ", " + key;
448 }
449 csv_string += "\n";
450
451 for (var i = 0; i < performance_measurements.length; i++) {
452 var performance_measurement = performance_measurements[i];
453 csv_string += performance_measurement.tpf;
454 csv_string += ", " + performance_measurement.fps;
455
456 for (var key in data_frame) {
457 csv_string += ", " + data_frame[key][i];
458 }
459 csv_string += "\n";
460 }
461
462 return csv_string;
463 }
464
465 function drawCSVString(csv_string) {
466 var div = document.getElementById("csv_string");
467 div.innerHTML = csv_string;
468 }
469
470 // =========================================================================//
471 // Utility functions to set canvas parameters and perform canvas draw calls //
472 // =========================================================================//
473
474 function drawShape(context, shape_type, size) {
475 context.lineWidth = 3;
476 switch (shape_type) {
477 case SHAPE_TYPE.STROKE_PATH:
478 var radius = size/2;
479 context.beginPath();
480 context.ellipse(400, 400, radius, radius, 0, 0, 2*Math.PI, false);
481 context.stroke();
482 return new DrawSize(size, size);
483
484 case SHAPE_TYPE.FILL_CONVEX_PATH:
485 var radius = size/2;
486 context.beginPath();
487 context.ellipse(400, 400, radius, radius, 0, 0, 2*Math.PI, false);
488 context.fill();
489 return new DrawSize(size, size);
490
491 case SHAPE_TYPE.FILL_NON_CONVEX_PATH:
492 var radius = size/2;
493 context.beginPath();
494 context.lineTo(400, 400);
495 context.arc(400, 400, radius, 0, Math.PI*3/2, false);
496 context.lineTo(this.x, this.y);
497 context.fill();
498 return new DrawSize(size, size);
499
500 case SHAPE_TYPE.STROKE_RECT:
501 context.beginPath();
502 context.rect(100, 100, size, size);
503 context.stroke();
504 return new DrawSize(size, size);
505
506 case SHAPE_TYPE.FILL_RECT:
507 context.beginPath();
508 context.rect(100, 100, size, size);
509 context.fill();
510 return new DrawSize(size, size);
511
512 case SHAPE_TYPE.STROKE_TEXT:
513 context.font = size + "px Georgia";
514 context.strokeText("M", 40, 500);
515 return new DrawSize(context.measureText("M").width, size);
516
517 case SHAPE_TYPE.FILL_TEXT:
518 context.font = size + "px Georgia";
519 context.fillText("M", 40, 500);
520 return new DrawSize(context.measureText("M").width, size);
521
522 default:
523 console.log("Invalid SHAPE_TYPE: " + shape_type);
524 return new DrawSize(0, 0);
525 }
526 }
527
528 var svg_image = document.getElementById("svg_image_element");
529 var png_image = document.getElementById("png_image_element");
530 function drawImageType(context, image_type, size) {
531 switch (image_type) {
532 case IMAGE_TYPE.DRAW_SVG_IMAGE:
533 context.drawImage(svg_image, 50, 50, size, size);
534 return new DrawSize(size, size);
535
536 case IMAGE_TYPE.DRAW_PNG_IMAGE:
537 if (size > png_image.width) {
538 throw "In drawImageType (png) - size too big ";
539 }
540 if (size > png_image.height) {
541 throw "In drawImageType (png) - size too big ";
542 }
543 context.drawImage(png_image, 0, 0, size, size, 50, 50, size, size);
544 return new DrawSize(size, size);
545 }
546 }
547
548 function setShapeStyle(context, shape_style, area) {
549 var gradient = "";
550 switch (shape_style) {
551 case SHAPE_STYLE.LINEAR_GRADIENT:
552 var gradient = context.createLinearGradient(0, 0, 800, 600);
553 gradient.addColorStop(0, "blue");
554 gradient.addColorStop(1, "white");
555 context.fillStyle = gradient;
556 context.strokeStyle = gradient;
557 break;
558 case SHAPE_STYLE.COLOR:
559 context.fillStyle = "blue";
560 context.strokeStyle = "blue";
561 break;
562 case SHAPE_STYLE.RADIAL_GRADIENT:
563 var gradient =
564 context.createRadialGradient(400, 300, 100, 400, 300, 800);
565 gradient.addColorStop(0, "blue");
566 gradient.addColorStop(1, "white");
567 context.fillStyle = gradient;
568 context.strokeStyle = gradient;
569 break;
570 case SHAPE_STYLE.PATTERN:
571 var pattern = context.createPattern(png_image,"repeat");
572 context.fillStyle = pattern;
573 context.strokeStyle = pattern;
574 break;
575 default:
576 console.log("Invalid SHAPE_STYLE: " + shape_style);
577 break;
578 }
579 }
580
581 function setShadeStyle(context, shadow_blur) {
582 context.shadowBlur = shadow_blur;
583 context.shadowColor= "black";
584 }
585
586 function getImageData(context, size) {
587 var image = context.getImageData(10,10, size, size);
588 return new DrawSize(size, size);
589 }
590
591
592 var image_data_cache = {};
593 function putImageData(context, size) {
594 if (!(size in image_data_cache)) {
595 // Cache a data source so that subsequent calls with the same size don't
596 // need to generate a new data source of the appropriate size
597 image_data_cache[size] = context.createImageData(size, size);
598 }
599
600 context.putImageData(image_data_cache[size], 10, 10);
601 return new DrawSize(size, size);
602 }
603
604 // ================//
605 // Other utilities //
606 // ================//
607
608 function RecordingHelper() {
609 this.num_frames_since_restart = 0;
610 this.recording_start_time = Date.now();
611
612 this.restartRecordingTime = function() {
613 this.num_frames_since_restart = 0;
614 this.recording_start_time = Date.now();
615 }
616
617 this.recordFrame = function() {
618 this.num_frames_since_restart += 1;
619 }
620
621 this.getAverageTimePerFrame = function() {
622 var time_interval = Date.now() - this.recording_start_time;
623 return time_interval / this.num_frames_since_restart;
624 }
625
626 this.getAverageFramesPerSecond = function() {
627 return 1000 / this.getAverageTimePerFrame();
628 }
629
630 this.getNumberOfFramesRecorded = function() {
631 return this.num_frames_since_restart;
632 }
633 }
634
635 function getTrueWithProbability(probability) {
636 return Math.random() < probability;
637 }
638
639 // Given the current quantity of a call, the current time per frame and target
640 // time per frame, return a suggested quantity that should yield a rendering
641 // speed closer to the target time per frame assuming that the time per frame
642 // is linearly proportional to the quantity.
643 function quantityForTarget(current_quantity, current_tpf, target_tpf) {
644 if (current_quantity <= 0) return 1;
645 return Math.round(current_quantity * target_tpf / current_tpf);
646 }
647
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698