Add a graphics benchmark
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 5 Oct 2015 20:38:52 +0000 (20:38 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 5 Oct 2015 20:38:52 +0000 (20:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=149053
<rdar://problem/18984169>

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2015-10-05
Reviewed by Dean Jackson.

Instead of measuring the FPS of the animation, this benchmark measures the
test complexity when rendering at a set-point FPS which should be lower
than 60 FPS. This benchmark tries to stay at the set-point FPS by using
a closed loop control system PID function. The gain of the system is passed
as a parameter when running the test. Measuring the FPS faithfully results
very fluctuating values. A Kalman filter is used to give a better estimate
for the current FPS.

The animation of the tests is done manually. requestAnimationFrame() is
called with a callback. Inside this callback, the test is animating by
changing the positions of the elements inside the page. The test complexity
may change also if the current FPS is not equal to the desired FPS.

In this patch, the benchmark and the tests are included. The shared code
and the tests runner are included in separate patches.

* Animometer/runner/animometer.html:
* Animometer/runner/resources/animometer.js:
Add two new examples for more complex animation techniques.
Add an option to show/hide the test running results which is off by default.

* Animometer/runner/resources/tests.js: Added.
(suiteFromName): Returns a suite given its name.
(testFromName): Returns a test given its suite and name.

* Animometer/tests: Added.
This directory includes all the test suites to be run by the benchmark.
runner. All the tests should try to run on three stages: CSS, canvas and
SVG.

* Animometer/tests/bouncing-particles: Added.
* Animometer/tests/bouncing-particles/resources: Added.
The bouncing particles test is an example of a simple animation technique.

* Animometer/tests/bouncing-particles/bouncing-canvas-images.html: Added.
* Animometer/tests/bouncing-particles/bouncing-canvas-shapes.html: Added.
* Animometer/tests/bouncing-particles/bouncing-css-images.html: Added.
* Animometer/tests/bouncing-particles/bouncing-css-shapes.html: Added.
* Animometer/tests/bouncing-particles/bouncing-svg-images.html: Added.
* Animometer/tests/bouncing-particles/bouncing-svg-shapes.html: Added.
Bouncing particles test pages.

* Animometer/tests/bouncing-particles/resources/bouncing-particles.js: Added.
(BouncingParticle): Base class for a bouncing particle.
(BouncingParticle.prototype.center): Returns the center point or the particle.
(BouncingParticle.prototype.animate): Moves the particle based on its current position, angle and velocity.

(BouncingParticlesAnimator): A sub class of Animator.

(BouncingParticlesStage): Represents the container of all the bouncing particles.
(BouncingParticlesStage.prototype.parseShapeParamters): Gets the shape parameters for shape animating tests.
(BouncingParticlesStage.prototype.randomRotater): Creates a rotater for the particles.
(BouncingParticlesStage.prototype.animate): Animates all the particles.
(BouncingParticlesStage.prototype.tune): Changes the test by adding or removing particles.

(BouncingParticlesBenchmark): Runs the benchmark for bouncing particles test.
(BouncingParticlesBenchmark.prototype.createAnimator): Creates an animator of type BouncingParticlesAnimator.

* Animometer/tests/bouncing-particles/resources/bouncing-css-shapes.js: Added.
(BouncingCssShape): A sub class of BouncingParticle for animating CSS shapes.
(BouncingCssShape.prototype._createSpan): Creates a <span> element and takes the shape and clipping classes into consideration.
(BouncingCssShape.prototype._move): Moves the particle to a new location. Apply transform since it does not require layout.
(BouncingCssShape.prototype.animate): Rotates and moves the shape to a new location.

(BouncingCssShapesStage): A sub class of BouncingParticlesStage for animating CSS shapes.
(BouncingCssShapesStage.prototype.createParticle): Creates a particle of type BouncingCssShape.
(BouncingCssShapesStage.prototype.particleWillBeRemoved): Removes the corresponding element form the parent children list.

(BouncingCssShapesBenchmark): A sub class of BouncingParticlesBenchmark for animating CSS shapes.
(BouncingCssShapesBenchmark.prototype.createStage): Creates a stage of type BouncingCssShapesStage.
(window.benchmarkClient.create): Creates a benchmark of type BouncingCssShapesBenchmark.

* Animometer/tests/bouncing-particles/resources/bouncing-css-images.js: Added.
(BouncingCssImage): A sub class of BouncingParticle for animating CSS images.
(BouncingCssImage.prototype._move): Move the particle to a new location. Apply transform since it does not require layout.
(BouncingCssImage.prototype.animate): Rotates and moves the shape to a new location.

(BouncingCssImagesStage): A sub class of BouncingParticlesStage for animating CSS images.
(BouncingCssImagesStage.prototype.createParticle): Creates a particle of type BouncingCssImage.
(BouncingCssImagesStage.prototype.particleWillBeRemoved): Removes the corresponding element form the parent children list.

(BouncingCssImagesBenchmark): A sub class of BouncingParticlesBenchmark for animating CSS images.
(BouncingCssImagesBenchmark.prototype.createStage): Creates a stage of type BouncingCssImagesStage.
(window.benchmarkClient.create): Creates a benchmark of type BouncingCssImagesBenchmark.

* Animometer/tests/bouncing-particles/resources/bouncing-canvas-particles.js: Added.
(BouncingCanvasParticle): A base sub-class of BouncingParticle for animating canvas particles.
(BouncingCanvasParticle.prototype._applyRotation): Apply the particle rotation-around-center transform to the canvas context.
(BouncingCanvasParticle.prototype._applyClipping): Apply the particle clipping to the canvas context.
(BouncingCanvasParticle.prototype._draw): A non-implemented version of the drawing function.
(BouncingCanvasParticle.prototype.animate): Carries out all the steps to redraw the canvas particle.

(BouncingCanvasParticlesStage): A base sub-class of BouncingParticlesStage for animating canvas particles.

(BouncingCanvasParticlesAnimator): A concrete sub-class of BouncingParticlesAnimator for animating canvas particles.
(BouncingCanvasParticlesAnimator.prototype.animate): Overrides the base class method to clear the canvas before redrawing the stage.

(BouncingCanvasParticlesBenchmark): A base sub-class of StageBenchmark for animating canvas particles.
(BouncingCanvasParticlesBenchmark.prototype.createAnimator): Creates the canvas particles animator.

* Animometer/tests/bouncing-particles/resources/bouncing-canvas-shapes.js: Added.
(BouncingCanvasShape): A concrete sub-class of BouncingCanvasParticle for animating canvas shapes.
(BouncingCanvasShape.prototype._applyFill): Sets the fillStyle in the canvas context.
(BouncingCanvasShape.prototype._drawShape): Carries out the actual drawing.
(BouncingCanvasShape.prototype._draw): Carries out all the steps to draw the shape.

(BouncingCanvasShapesStage): A concrete sub-class of BouncingCanvasParticle for animating canvas shapes.
(BouncingCanvasShapesStage.prototype.createParticle): Creates a particle of type BouncingCanvasShape.

(BouncingCanvasShapesBenchmark): A concrete sub-class of BouncingCanvasParticlesBenchmark for animating canvas shapes.
(BouncingCanvasShapesBenchmark.prototype.createStage): Creates a stage of type BouncingCanvasShapesStage.
(window.benchmarkClient.create): Creates a benchmark of type BouncingCanvasShapesBenchmark.

* Animometer/tests/bouncing-particles/resources/bouncing-canvas-images.js: Added.
(BouncingCanvasImage): A concrete sub-class of BouncingCanvasParticle for animating canvas images.
(BouncingCanvasImage.prototype._draw): Draws an image on the context of a canvas.

(BouncingCanvasImagesStage): A concrete sub-class of BouncingCanvasParticlesBenchmark for animating canvas images.
(BouncingCanvasImagesStage.prototype.createParticle): Creates a particle of type BouncingCanvasImage.

(BouncingCanvasImagesBenchmark): A concrete sub-class of BouncingCanvasParticlesBenchmark for animating canvas images.
(BouncingCanvasImagesBenchmark.prototype.createStage): Creates a stage of type BouncingCanvasImagesStage.
(window.benchmarkClient.create): Creates a benchmark of type BouncingCanvasImagesBenchmark.

* Animometer/tests/bouncing-particles/resources/bouncing-svg-particles.js: Added.
(BouncingSvgParticle): A base sub-class of BouncingParticle for animating SVG particles.
(BouncingSvgParticle.prototype._applyClipping): Apply the particle clipping by setting the 'clip-path' attribute of the SVGElement.
(BouncingSvgParticle.prototype._move): Moves the particle to a new location. Apply transform since it does not require layout.
(BouncingSvgParticle.prototype.animate): Rotates and moves the shape to a new location.
(BouncingSvgParticlesStage): A sub class of BouncingParticlesStage for animating SVGElements.
(BouncingSvgParticlesStage.prototype._createDefs): Creates an SVGDefsElement.
(BouncingSvgParticlesStage.prototype._ensureDefsIsCreated): Ensures there is only one SVGDefsElement is created.
(BouncingSvgParticlesStage.prototype._createClipStar): Creates an SVGClipPathElement and sets its 'd' attribute to a star like shape.
(BouncingSvgParticlesStage.prototype.ensureClipStarIsCreated): Ensure there is only one star SVGClipPathElement is created.
(BouncingSvgParticlesStage.prototype.particleWillBeRemoved): Remove the corresponding element form the parent children list.

* Animometer/tests/bouncing-particles/resources/bouncing-svg-shapes.js: Added.
(BouncingSvgShape): A concrete sub-class of BouncingSVGParticle for animating SVG shapes.
(BouncingSvgShape.prototype._createShape): Creates an SVG shape.
(BouncingSvgShape.prototype._applyFill): Applies the selected fill style to the SVG shape.

(BouncingSvgShapesStage): A concrete sub-class of BouncingSvgParticlesStage for animating SVG shapes.
(BouncingSvgShapesStage.prototype.createGradient): Creates an SVGLinearGradientElement.
(BouncingSvgShapesStage.prototype.createParticle): Creates a particle of type BouncingSvgShape.
(BouncingSvgShapesStage.prototype.particleWillBeRemoved): Ensures the attached SVGLinearGradientElement is removed from the SVGDefsElement.

(BouncingSvgShapesBenchmark): A concrete sub-class of BouncingParticlesBenchmark for animating SVG images.
(BouncingSvgShapesBenchmark.prototype.createStage): Creates a stage of type BouncingSvgShapesStage.
(window.benchmarkClient.create): Creates a benchmark of type BouncingSvgShapesBenchmark.

* Animometer/tests/bouncing-particles/resources/bouncing-svg-images.js: Added.
(BouncingSvgImage): A concrete sub-class of BouncingSVGParticle for animating SVG images.

(BouncingSvgImagesStage): A concrete sub-class of BouncingSVGParticlesBenchmark for animating SVG images.
(BouncingSvgImagesStage.prototype.createParticle): Creates a particle of type BouncingSvgImage.

(BouncingSvgImagesBenchmark): A concrete sub-class of BouncingParticlesBenchmark for animating SVG images.
(BouncingSvgImagesBenchmark.prototype.createStage): Creates a stage of type BouncingSvgImagesStage.
(window.benchmarkClient.create): Creates a benchmark of type BouncingSvgImagesBenchmark.

* Animometer/tests/examples: Added.
* Animometer/tests/examples/canvas-electrons.html: Added.
* Animometer/tests/examples/canvas-stars.html: Added.
Examples test pages.

* Animometer/tests/examples/resources: Added.
* Animometer/tests/examples/resources/canvas-electrons.js: Added.
(CanvasElectron): An object which draws and animate a electron object on a canvas stage.
(CanvasElectron.prototype._draw): Draws the electron object.
(CanvasElectron.prototype.animate): Animates the electron object.

(CanvasElectronsStage): A concrete sub-class of Stage for animating electrons.
(CanvasElectronsStage.prototype.tune): Changes the test by adding or removing elements.
(CanvasElectronsStage.prototype.animate): Animates the test elements.

(CanvasElectronsAnimator): A concrete sub-class of StageAnimator for animating canvas electrons.
(CanvasElectronsAnimator.prototype.animate): Overrides the base class method to clear the canvas before redrawing the stage.

(CanvasElectronsBenchmark): A concrete sub-class of StageBenchmark for animating electrons.
(CanvasElectronsBenchmark.prototype.createStage): Creates a stage of CanvasElectronsStage.
(CanvasElectronsBenchmark.prototype.createAnimator): Creates an animator of type CanvasElectronsAnimator.
(window.benchmarkClient.create): Creates a benchmark of type CanvasElectronsBenchmark.

* Animometer/tests/examples/resources/canvas-stars.js: Added.
(CanvasStar): An object which draws and animate a star object on a canvas stage.
(CanvasStar.prototype._draw): Draws the star object.
(CanvasStar.prototype.animate): Animates the star object.

(CanvasStarsStage): A concrete sub-class of Stage for animating stars.
(CanvasStarsStage.prototype.tune): Changes the test by adding or removing elements.
(CanvasStarsStage.prototype.animate): Animates the test elements.

(CanvasStarsAnimator): A concrete sub-class of StageAnimator for animating canvas stars.
(CanvasStarsAnimator.prototype.animate): Overrides the base class method to clear the canvas before redrawing the stage.

(CanvasStarsBenchmark): A concrete sub-class of Benchmark for animating stars.
(CanvasStarsBenchmark.prototype.createStage): Creates a stage of CanvasStarsStage.
(CanvasStarsBenchmark.prototype.createAnimator): Creates an animator of type CanvasStarsAnimator.
(window.benchmarkClient.create): Creates a benchmark of type CanvasStarsBenchmark.

* Animometer/tests/resources: Added.
This directory includes the script which is required to run an adaptive
graphics benchmark. From an empty test page, the set of classes in this
directory are responsible for measuring the current frame rate and
changing the test to reach a desired FPS. It keeps asking the test page
to tune itself by a certain value to increase or decrease the frame rate.
It's also responsible for sampling the test state and the corresponding
frame rate.

* Animometer/tests/resources/main.js: Added.
(BenchmarkState): Tracks the state of the benchmark test.
(BenchmarkState.prototype._timeOffset): Returns the timeOffset of a stage.
(BenchmarkState.prototype._message): Returns the message of a stage.
(BenchmarkState.prototype.update): Sets the currentTimeOffset to a new value.
(BenchmarkState.prototype.samplingTimeOffset): Returns the timeOffset of the sampling stage.
(BenchmarkState.prototype.currentStage): Returns the current stage of the benchmark.
(BenchmarkState.prototype.currentMessage): Returns the message of the current stage and timeOffset.
(BenchmarkState.prototype.currentProgress): Returns a percentage of how much the benchmark is running.

(Animator): Manages animating the test.
(Animator.prototype.start): Called if animating using setInterval is requested.
(Animator.prototype.timeDelta): Returns the current timeDelta
(Animator.prototype.animate): Manages the test animation.
(Animator.prototype.animateLoop): Called if animating using requestAnimationFrame is requested.

(Benchmark): Manages running the test benchmark and recording the sampled data.
(Benchmark.prototype.start): Starts the benchmark.
(Benchmark.prototype.update): Called from the animator.animate() to change the complexity of the test.
(Benchmark.prototype.record): Shows the current (not final) results of the benchmark.
(Benchmark.prototype.resolveWhenFinished): Spins until the benchmark is finished and returns its results.
(Benchmark.prototype.run): Starts the test, runs it, waits until it is finished and return its results.
(window.runBenchmark): Called from the benchmark runner through the suite controller run-callback.

* Animometer/tests/resources/math.js: Added.
(Matrix): A matrix object.
(Vector3): A vector of size 3 object.
(Matrix3): A matrix of size 3x3 object.

(PIDController): Closed-loop controller for a set-point y.
(PIDController.prototype._sat): Limits the output to a certain range.
(PIDController.prototype.tune): Given the current output of a system, it produces a new pid value for tuning it.

(KalmanEstimator): Implement Kalman filter to get an estimate for a sampled data point.
(KalmanEstimator.prototype.estimate): Returns an estimate for for a sampled data point.

* Animometer/tests/resources/utilities.js: Added.
(window.Utilities._parse): Given a separator character, it pareses a string to a set of <key, value> pairs.
(window.Utilities.parseParameters): Parses a test parameters.
(window.Utilities.parseArguments): Parses a tag arguments.
(window.Utilities.extendObject): Adds the attributes and their values of an object to another object.
(window.Utilities.copyObject): Copies the attributes and their values of an object to a new object.
(window.Utilities.mergeObjects): Copies the attributes and their values of two objects to a new object.
(window.Utilities.createSvgElement): Creates an SVGElement given its name and its attributes.

* Animometer/tests/resources/stage.css: Added.
* Animometer/tests/resources/stage.js: Added.
(Rotater): Manages rotating an angle within a fixed time interval.
(Rotater.prototype.get interval): Returns the time interval which is required to rotate 360 degrees.
(Rotater.prototype.next): Moves the current time by a delta.
(Rotater.prototype.degree): Returns the current rotating degree.
(Rotater.prototype.rotateZ): Returns CSS formatted transform rotateZ() string for the current degree.
(Rotater.prototype.rotate): Returns SVG formatted transform rotate() string for the current degree.

(Stage): A base class for managing the test complexity and test animation.
(Stage.prototype.get size): Returns the size of the stage excluding the CSS padding.
(Stage.prototype.random): Returns a random float.
(Stage.prototype.randomInt): Returns a random integer.
(Stage.prototype.randomPosition): Returns a random position.
(Stage.prototype.randomSquareSize): Returns a square size.
(Stage.prototype.randomVelocity): Returns a random velocity.
(Stage.prototype.randomAngle): Returns a random angle.
(Stage.prototype.randomColor): Returns a random color not too dark and not too light.
(Stage.prototype.randomRotater): Creates a random rotater. Its velocity depends on choosing a random rotation time interval.
(Stage.prototype.tune): A not-implemented version of this function.
(Stage.prototype.animate): A not-implemented version of this function.
(Stage.prototype.clear): Clears the stage from all its animation elements.

(StageAnimator): A base class for the stage-based animators.
(StageAnimator.prototype.animate): Calls Animator.animate() which updates the test page and then calls Stage.animate() to force redraw.

(StageBenchmark): A base class for the stage-based benchmarks.
(StageBenchmark.prototype.createStage): Creates the default stage.
(StageBenchmark.prototype.createAnimator): Creates the default animator.
(StageBenchmark.prototype.tune): Delegates the call to stage.
(StageBenchmark.prototype.clear): Delegates the call to stage.
(StageBenchmark.prototype.showResults): Shows the results/progress through its recordTable and progressBar.

* Animometer/tests/resources/yin-yang.png: Added.
* Animometer/tests/resources/yin-yang.svg: Added.
These images are shared among all the tests.

* Animometer/tests/template: Added.
* Animometer/tests/template/resources: Added.
This directory includes template tests which do nothing. They can be used
to author new tests. Animated items can be created, moved and redrawn by
removing the TODO comments in the script files and writing actual code.

* Animometer/tests/template/template-css.html: Added.
* Animometer/tests/template/template-canvas.html: Added.
* Animometer/tests/template/template-svg.html: Added.
Template test pages. They can be used as they are. CSS attributes or hidden
elements can be added to these derived test pages if needed.

* Animometer/tests/template/resources/template-css.js: Added.

(TemplateCssStage): A stage to create and animate HTMLElements.
(TemplateCssStage.prototype.tune): Changes the test by adding or removing elements.
(TemplateCssStage.prototype.animate): Animates the test elements.
(TemplateCssBenchmark):
(TemplateCssBenchmark.prototype.createStage): Creates the test stage.
(window.benchmarkClient.create): Creates a benchmark of type TemplateCssBenchmark.

* Animometer/tests/template/resources/template-canvas.js: Added.
(TemplateCanvasObject):
(TemplateCanvasObject.prototype._draw): Draws the objects on the canvas context.
(TemplateCanvasObject.prototype.animate): Moves and redraws the object.
(TemplateCanvasStage): A stage to create and animate drawing elements.
(TemplateCanvasStage.prototype.tune): hanges the test by adding or removing elements.
(TemplateCanvasStage.prototype.animate):  Animates the test elements.
(TemplateCanvasAnimator.prototype.animate): Starts the animation every frame.
(TemplateCanvasBenchmark):
(TemplateCanvasBenchmark.prototype.createStage): Creates a stage of type TemplateCanvasStage.
(TemplateCanvasBenchmark.prototype.createAnimator): Creates a animator of type TemplateCanvasAnimator.
(window.benchmarkClient.create): Creates a benchmark of type TemplateCanvasBenchmark.

* Animometer/tests/template/resources/template-svg.js: Added.
(TemplateSvgStage): A stage to create and animate SVGElements.
(TemplateSvgStage.prototype.tune): Changes the test by adding or removing elements.
(TemplateSvgStage.prototype.animate): Animates the test elements.
(TemplateSvgBenchmark.prototype.createStage): Creates a stage of type TemplateSvgStage.
(window.benchmarkClient.create): Creates a benchmark of type TemplateSvgBenchmark.

* Animometer/tests/text: Added.
* Animometer/tests/text/resources: Added.
This directory includes the text animating tests which currently runs
on CSS stage only.

* Animometer/tests/text/layering-text.html: Added.
Text test page.

* Animometer/tests/text/resources/layering-text.js: Added.
(LayeringTextStage): Represents the container of all the stacked text layers.
(LayeringTextStage.parseTextItem): Parses a textItem which may be an opening tag, a closing tag or a self-closing tag.
(LayeringTextStage.isOpeningTextItem): Returns true if the textItem is an opening tag e.g. '<ol>'.
(LayeringTextStage.isClosingTextItem): Returns true if  the textItem is an closing tag e.g. '</ol>.
(LayeringTextStage.textItemsFlags.LayeringTextStage.textItems.map): Calculates and stores isOpening and isClosing flags for each textItem.
(LayeringTextStage.isColorableTextItem): Returns true if the textItem is self-closing tag e.g. '<p>...</p>'.
(LayeringTextStage.isInsertableTextItem): Returns true if the textItems causes a new element to be added to the text layers.
(LayeringTextStage.colorableTextItems.LayeringTextStage.textItemsFlags.filter): Number of colorable textItems.
(LayeringTextStage.insertableTextItems.LayeringTextStage.textItemsFlags.filter): Number of insertable textItems.
(LayeringTextStage.colorIndexToTextElementIndex): Maps from colorIndex [0..colorableTextItems-1] to textElementIndex [0..insertableTextItems-1].
(LayeringTextStage.prototype._nextTextItem): Moves the _textItemIndex one step forward in a loop [0..LayeringTextStage.textItems.length-1].
(LayeringTextStage.prototype._previousTextItem): Moves the _textItemIndex one step backward in a loop.
(LayeringTextStage.prototype._pushTextElement): Creates a new textItemElement and adds it to the topmost container <div>.
(LayeringTextStage.prototype._popTextElement): Removes the last textItemElement from the topmost container <div>.
(LayeringTextStage.prototype._colorTextItem): Changes the background color of a single colorable textElement. The index advances in a circle [0..colorableTextItems-1].
(LayeringTextStage.prototype.animate): Changes the background color and the text color of the textElements such that a redraw is enforced.
(LayeringTextStage.prototype.tune): Adds or removes textElements to the stage.

(LayeringTextBenchmark): Runs the benchmark for the layering text test.
(LayeringTextBenchmark.prototype.createStage): Creates a stage of type LayeringTextStage.
(window.benchmarkClient.create): Creates a benchmark of type LayeringTextBenchmark.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@190575 268f45cc-cd09-0410-ab3c-d52691b4dbfc

38 files changed:
PerformanceTests/Animometer/runner/animometer.html
PerformanceTests/Animometer/runner/resources/animometer.js
PerformanceTests/Animometer/runner/resources/tests.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-canvas-images.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-canvas-shapes.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-css-images.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-css-shapes.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-svg-images.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/bouncing-svg-shapes.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-canvas-images.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-canvas-particles.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-canvas-shapes.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-css-images.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-css-shapes.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-particles.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-images.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-particles.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-shapes.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/examples/canvas-electrons.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/examples/canvas-stars.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/examples/resources/canvas-electrons.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/examples/resources/canvas-stars.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/resources/main.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/resources/math.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/resources/stage.css [new file with mode: 0644]
PerformanceTests/Animometer/tests/resources/stage.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/resources/utilities.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/resources/yin-yang.png [new file with mode: 0644]
PerformanceTests/Animometer/tests/resources/yin-yang.svg [new file with mode: 0644]
PerformanceTests/Animometer/tests/template/resources/template-canvas.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/template/resources/template-css.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/template/resources/template-svg.js [new file with mode: 0644]
PerformanceTests/Animometer/tests/template/template-canvas.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/template/template-css.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/template/template-svg.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/text/layering-text.html [new file with mode: 0644]
PerformanceTests/Animometer/tests/text/resources/layering-text.js [new file with mode: 0644]
PerformanceTests/ChangeLog

index e728e9a..6ea78ac 100644 (file)
@@ -2,7 +2,6 @@
 <html>
 <head>
     <link rel="stylesheet" href="resources/animometer.css">
-    <script src="../resources/statistics.js" defer></script>
     <script src="../resources/sampler.js" defer></script>
     <script src="../resources/extensions.js" defer></script>
     <script src="resources/tests.js" defer=""></script>
@@ -24,6 +23,7 @@
                     <label><input id="html-suite" type="checkbox" checked> HTML elements suite</label><br>
                     <label><input id="canvas-suite" type="checkbox" checked> Canvas drawings suite</label><br>
                     <label><input id="svg-suite" type="checkbox" checked> SVG elements suite</label><br>
+                    <label><input id="examples-suite" type="checkbox"> Examples suite</label><br>
                     <label><input id="template-suite" type="checkbox"> Template suite</label>
                 </div>
                 <div>
@@ -31,6 +31,7 @@
                     <label>Frame rate: <input id="frame-rate" type="number" value="50"> fps</label><br>
                     <label><input id="estimated-frame-rate" type="checkbox" checked> Estimated Frame Rate</label><br>
                     <label><input id="fix-test-complexity" type="checkbox"> Fix test complexity after warmup</label>
+                    <label><input id="show-running-results" type="checkbox"> Show running results</label>
                 </div>
             </div>
             <div class="buttons">
index acbbc52..42c102f 100644 (file)
@@ -63,6 +63,7 @@ function startBenchmark()
         document.getElementById("html-suite"), 
         document.getElementById("canvas-suite"), 
         document.getElementById("svg-suite"),
+        document.getElementById("examples-suite"),
         document.getElementById("template-suite"),
     ];
     var enabledSuites = Suites.filter(function (suite, index) { return !suite.disabled && checkboxes[index].checked; });
@@ -72,6 +73,11 @@ function startBenchmark()
     benchmarkRunnerClient.options["frameRate"] = parseInt(document.getElementById("frame-rate").value);
     benchmarkRunnerClient.options["estimatedFrameRate"] = document.getElementById("estimated-frame-rate").checked;    
     benchmarkRunnerClient.options["fixTestComplexity"] = document.getElementById("fix-test-complexity").checked;
+    benchmarkRunnerClient.options["showRunningResults"] = document.getElementById("show-running-results").checked;
+    
+    if (!benchmarkRunnerClient.options["showRunningResults"])
+        document.getElementById("record").style.display = "none";
+
     var runner = new BenchmarkRunner(enabledSuites, benchmarkRunnerClient);
     runner.runMultipleIterations(benchmarkRunnerClient.iterationCount);
 }
diff --git a/PerformanceTests/Animometer/runner/resources/tests.js b/PerformanceTests/Animometer/runner/resources/tests.js
new file mode 100644 (file)
index 0000000..6f77dd7
--- /dev/null
@@ -0,0 +1,230 @@
+var Titles = [
+    {
+        text: "Test Name",
+        width: 28,
+        children: []
+    },
+    {
+        text: "Animated Items",
+        width: 28,
+        children:
+        [
+            { text:   "Avg.", width: 7, children: [] },
+            { text:   "W.5%", width: 7, children: [] },
+            { text:   "Std.", width: 7, children: [] },
+            { text:      "%", width: 7, children: [] },
+        ]
+    },
+    {
+        text: "FPS",
+        width: 28,
+        children:
+        [
+            { text:   "Avg.", width: 7, children: [] },
+            { text:   "W.5%", width: 7, children: [] },
+            { text:   "Std.", width: 7, children: [] },
+            { text:      "%", width: 7, children: [] },
+        ]
+    },
+    {
+        text: "Score",
+        width: 8,
+        children: []
+    },
+    {
+        text: "Samples",
+        width: 8,
+        children: []
+    }
+];
+
+var Suites = [];
+
+Suites.push({
+    name: "HTML Bouncing Particles",
+    prepare: function(runner, contentWindow, contentDocument)
+    {
+        return runner.waitForElement("#stage").then(function (element) {
+            return element;
+        });
+    },
+    
+    run: function(contentWindow, test, options, recordTable, progressBar)
+    {
+        return contentWindow.runBenchmark(this, test, options, recordTable, progressBar);
+    },
+
+    titles: Titles,
+    tests: [
+        { 
+            url: "../tests/bouncing-particles/bouncing-css-shapes.html?gain=1&addLimit=100&removeLimit=5&particleWidth=12&particleHeight=12&shape=circle",
+            name: "CSS bouncing circles"
+        },
+        { 
+            url: "../tests/bouncing-particles/bouncing-css-shapes.html?gain=1&addLimit=100&removeLimit=5&particleWidth=40&particleHeight=40&shape=rect&clip=star",
+            name: "CSS bouncing clipped rects"
+        },
+        { 
+            url: "../tests/bouncing-particles/bouncing-css-shapes.html?gain=1&addLimit=100&removeLimit=5&particleWidth=50&particleHeight=50&shape=circle&fill=gradient",
+            name: "CSS bouncing gradient circles"
+        },
+        {
+            url: "../tests/bouncing-particles/bouncing-css-images.html?gain=0.4&addLimit=5&removeLimit=2&particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.svg",
+            name: "CSS bouncing SVG images"
+        },
+        {
+            url: "../tests/bouncing-particles/bouncing-css-images.html?gain=1&addLimit=100&removeLimit=5&particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.png",
+            name: "CSS bouncing PNG images"
+        },
+        {
+            url: "../tests/text/layering-text.html?gain=4&addLimit=100&removeLimit=100",
+            name: "CSS layering text"
+        },
+    ]
+});
+
+Suites.push({
+    name: "Canvas Bouncing Particles",
+    prepare: function(runner, contentWindow, contentDocument)
+    {
+        return runner.waitForElement("#stage").then(function (element) {
+            return element;
+        });
+    },  
+    
+    run: function(contentWindow, test, options, recordTable, progressBar)
+    {
+        return contentWindow.runBenchmark(this, test, options, recordTable, progressBar);
+    },
+    
+    titles: Titles,
+    tests: [
+        { 
+            url: "../tests/bouncing-particles/bouncing-canvas-shapes.html?gain=4&addLimit=100&removeLimit=1000&particleWidth=12&particleHeight=12&shape=circle",
+            name: "canvas bouncing circles"
+        },
+        { 
+            url: "../tests/bouncing-particles/bouncing-canvas-shapes.html?gain=4&addLimit=100&removeLimit=1000&particleWidth=40&particleHeight=40&shape=rect&clip=star",
+            name: "canvas bouncing clipped rects"
+        },
+        { 
+            url: "../tests/bouncing-particles/bouncing-canvas-shapes.html?gain=4&addLimit=100&removeLimit=1000&particleWidth=50&particleHeight=50&shape=circle&fill=gradient",
+            name: "canvas bouncing gradient circles"
+        },
+        { 
+            url: "../tests/bouncing-particles/bouncing-canvas-images.html?gain=0.4&addLimit=5&removeLimit=1&particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.svg",
+            name: "canvas bouncing SVG images"
+        },
+        {
+            url: "../tests/bouncing-particles/bouncing-canvas-images.html?gain=4&addLimit=1000&removeLimit=1000&particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.png",
+            name: "canvas bouncing PNG images"
+        },
+    ]
+});
+
+Suites.push({
+    name: "SVG Bouncing Particles",
+    prepare: function(runner, contentWindow, contentDocument)
+    {
+        return runner.waitForElement("#stage").then(function (element) {
+            return element;
+        });
+    },
+    
+    run: function(contentWindow, test, options, recordTable, progressBar)
+    {
+        return contentWindow.runBenchmark(this, test, options, recordTable, progressBar);
+    },
+    
+    titles: Titles,
+    tests: [
+        {
+            url: "../tests/bouncing-particles/bouncing-svg-shapes.html?gain=6&addLimit=100&removeLimit=1000&particleWidth=12&particleHeight=12&shape=circle",
+            name: "SVG bouncing circles",
+        },
+        {
+            url: "../tests/bouncing-particles/bouncing-svg-shapes.html?gain=0.6&addLimit=10&removeLimit=1&particleWidth=40&particleHeight=40&shape=rect&clip=star",
+            name: "SVG bouncing clipped rects",
+        },
+        {
+            url: "../tests/bouncing-particles/bouncing-svg-shapes.html?gain=0.8&addLimit=10&removeLimit=4&particleWidth=50&particleHeight=50&shape=circle&fill=gradient",
+            name: "SVG bouncing gradient circles"
+        },
+        {
+            url: "../tests/bouncing-particles/bouncing-svg-images.html?gain=0.4&addLimit=5&removeLimit=2&particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.svg",
+            name: "SVG bouncing SVG images"
+        },
+        {
+            url: "../tests/bouncing-particles/bouncing-svg-images.html?gain=4&addLimit=100&removeLimit=5&particleWidth=80&particleHeight=80&imageSrc=../resources/yin-yang.png",
+            name: "SVG bouncing PNG images"
+        },
+    ]
+});
+
+Suites.push({
+    name: "More complex examples",
+    prepare: function(runner, contentWindow, contentDocument)
+    {
+        return runner.waitForElement("#stage").then(function (element) {
+            return element;
+        });
+    },
+    
+    run: function(contentWindow, test, options, recordTable, progressBar)
+    {
+        return contentWindow.runBenchmark(this, test, options, recordTable, progressBar);
+    },
+    
+    titles: Titles,
+    tests: [
+        {
+            url: "../tests/examples/canvas-electrons.html?gain=1&addLimit=100&removeLimit=10",
+            name: "canvas electrons"
+        },
+        {
+            url: "../tests/examples/canvas-stars.html?gain=4&addLimit=100&removeLimit=5",
+            name: "canvas stars"
+        },
+    ]
+});
+
+Suites.push({
+    name: "Stage Templates (Can be used for new tests)",
+    prepare: function(runner, contentWindow, contentDocument)
+    {
+        return runner.waitForElement("#stage").then(function (element) {
+            return element;
+        });
+    },
+    
+    run: function(contentWindow, test, options, recordTable, progressBar)
+    {
+        return contentWindow.runBenchmark(this, test, options, recordTable, progressBar);
+    },
+    
+    titles: Titles,
+    tests: [
+        {
+            url: "../tests/template/template-css.html?gain=1&addLimit=100&removeLimit=5",
+            name: "CSS template"
+        },
+        {
+            url: "../tests/template/template-canvas.html?gain=1&addLimit=100&removeLimit=1000",
+            name: "canvas template"
+        },
+        {
+            url: "../tests/template/template-svg.html?gain=1&addLimit=100&removeLimit=5&<other_paramter>=<value>",
+            name: "SVG template"
+        },
+    ]
+});
+
+function suiteFromName(name)
+{
+    return Suites.find(function(suite) { return suite.name == name; });
+}
+
+function testFromName(suite, name)
+{
+    return suite.tests.find(function(test) { return test.name == name; });
+}
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-canvas-images.html b/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-canvas-images.html
new file mode 100644 (file)
index 0000000..9e5886c
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        img.hidden {
+            position: absolute;
+            visibility: hidden;
+        }
+    </style>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>  
+    <script src="resources/bouncing-particles.js"></script>
+    <script src="resources/bouncing-canvas-particles.js"></script>
+    <script src="resources/bouncing-canvas-images.js"></script>
+</head>
+<body>
+    <img class="hidden" src="../resources/yin-yang.svg">
+    <img class="hidden" src="../resources/yin-yang.png">
+    <canvas id="stage"></canvas>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-canvas-shapes.html b/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-canvas-shapes.html
new file mode 100644 (file)
index 0000000..4b1bc35
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>  
+    <script src="resources/bouncing-particles.js"></script>
+    <script src="resources/bouncing-canvas-particles.js"></script>
+    <script src="resources/bouncing-canvas-shapes.js"></script>
+</head>
+<body>
+    <canvas id="stage"></canvas>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-css-images.html b/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-css-images.html
new file mode 100644 (file)
index 0000000..f236420
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        img {
+            position: absolute;
+        }
+    </style>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>  
+    <script src="resources/bouncing-particles.js"></script>
+    <script src="resources/bouncing-css-images.js"></script>
+</head>
+<body>
+    <div id="stage"></div>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-css-shapes.html b/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-css-shapes.html
new file mode 100644 (file)
index 0000000..6693638
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        .circle {
+            position: absolute;
+            background-color: #000000;
+            border-radius: 50% 50%;
+        }
+        .rect {
+            position: absolute;
+            background-color: #000000;
+        }
+        .star {
+            -webkit-clip-path: polygon(50% 0%, 38% 38%, 0% 38%, 30% 60%, 18% 100%, 50% 75%, 82% 100%, 70% 60%, 100% 38%, 62% 38%);
+            -ms-clip-path: polygon(50% 0%, 38% 38%, 0% 38%, 30% 60%, 18% 100%, 50% 75%, 82% 100%, 70% 60%, 100% 38%, 62% 38%);  
+            clip-path: polygon(50% 0%, 38% 38%, 0% 38%, 30% 60%, 18% 100%, 50% 75%, 82% 100%, 70% 60%, 100% 38%, 62% 38%);
+        }
+    </style>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>  
+    <script src="resources/bouncing-particles.js"></script>
+    <script src="resources/bouncing-css-shapes.js"></script>
+</head>
+<body>
+    <div id="stage"></div>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-svg-images.html b/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-svg-images.html
new file mode 100644 (file)
index 0000000..1cd90cf
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>  
+    <script src="resources/bouncing-particles.js"></script>
+    <script src="resources/bouncing-svg-particles.js"></script>
+    <script src="resources/bouncing-svg-images.js"></script>
+</head>
+<body>
+    <svg id="stage"></svg>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-svg-shapes.html b/PerformanceTests/Animometer/tests/bouncing-particles/bouncing-svg-shapes.html
new file mode 100644 (file)
index 0000000..3c069fd
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>  
+    <script src="resources/bouncing-particles.js"></script>
+    <script src="resources/bouncing-svg-particles.js"></script>
+    <script src="resources/bouncing-svg-shapes.js"></script>
+</head>
+<body>
+    <svg id="stage"></svg>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-canvas-images.js b/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-canvas-images.js
new file mode 100644 (file)
index 0000000..258ccd7
--- /dev/null
@@ -0,0 +1,51 @@
+function BouncingCanvasImage(stage)
+{
+    BouncingCanvasParticle.call(this, stage);
+    this._imageElement = stage.imageElement;
+    this._shape = "image";
+}
+
+BouncingCanvasImage.prototype = Object.create(BouncingCanvasParticle.prototype);
+BouncingCanvasImage.prototype.constructor = BouncingCanvasImage;
+
+BouncingCanvasImage.prototype._draw = function()
+{
+    this._context.save();
+        this._applyRotation();
+        this._context.drawImage(this._imageElement, 0, 0, this._size.x, this._size.y);
+    this._context.restore();
+}
+
+function BouncingCanvasImagesStage(element, options)
+{
+    BouncingCanvasParticlesStage.call(this, element, options);
+
+    var imageSrc = options["imageSrc"] || "resources/yin-yang.svg";
+    this.imageElement = document.querySelector(".hidden[src=\"" + imageSrc + "\"]");
+}
+
+BouncingCanvasImagesStage.prototype = Object.create(BouncingCanvasParticlesStage.prototype);
+BouncingCanvasImagesStage.prototype.constructor = BouncingCanvasImagesStage;
+
+BouncingCanvasImagesStage.prototype.createParticle = function()
+{
+    return new BouncingCanvasImage(this);
+}
+
+function BouncingCanvasImagesBenchmark(suite, test, options, recordTable, progressBar)
+{
+    BouncingCanvasParticlesBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+BouncingCanvasImagesBenchmark.prototype = Object.create(BouncingCanvasParticlesBenchmark.prototype);
+BouncingCanvasImagesBenchmark.prototype.constructor = BouncingCanvasImagesBenchmark;
+
+BouncingCanvasImagesBenchmark.prototype.createStage = function(element)
+{
+    return new BouncingCanvasImagesStage(element, this._options);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    return new BouncingCanvasImagesBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-canvas-particles.js b/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-canvas-particles.js
new file mode 100644 (file)
index 0000000..63ad59b
--- /dev/null
@@ -0,0 +1,105 @@
+function BouncingCanvasParticle(stage)
+{
+    BouncingParticle.call(this, stage);
+    this._context = stage.context;
+    this._shape = "";
+    this._clip = stage.clip;
+}
+
+BouncingCanvasParticle.clips = {
+    star: [
+        new Point(0.50, 0.00),
+        new Point(0.38, 0.38),
+        new Point(0.00, 0.38),
+        new Point(0.30, 0.60),
+        new Point(0.18, 1.00),
+        new Point(0.50, 0.75),
+        new Point(0.82, 1.00),
+        new Point(0.70, 0.60),
+        new Point(1.00, 0.38),
+        new Point(0.62, 0.38)
+    ],
+};
+
+BouncingCanvasParticle.prototype = Object.create(BouncingParticle.prototype);
+BouncingCanvasParticle.prototype.constructor = BouncingCanvasParticle;
+
+BouncingCanvasParticle.prototype._applyRotation = function()
+{
+    if (this._shape == "circle")
+        return;
+
+    this._context.translate(this._size.x / 2, this._size.y / 2);
+    this._context.rotate(this._rotater.degree() * Math.PI / 180);
+    this._context.translate(-this._size.x / 2, -this._size.y / 2);
+}
+
+BouncingCanvasParticle.prototype._applyClipping = function()
+{
+    var clipPoints = BouncingCanvasParticle.clips[this._clip];
+    if (!clipPoints)
+        return;
+        
+    this._context.beginPath();
+    clipPoints.forEach(function(point, index) {
+        var point = this._size.multiply(point);
+        if (!index)
+            this._context.moveTo(point.x, point.y);
+        else
+            this._context.lineTo(point.x, point.y);
+    }, this);
+
+    this._context.closePath();
+    this._context.clip();
+}
+
+BouncingCanvasParticle.prototype._draw = function()
+{
+    throw "Not implemented";
+}
+
+BouncingCanvasParticle.prototype.animate = function(timeDelta)
+{
+    BouncingParticle.prototype.animate.call(this, timeDelta);
+    this._context.save();
+        this._context.translate(this._position.x, this._position.y);
+        this._draw();
+    this._context.restore();
+}
+
+function BouncingCanvasParticlesStage(element, options)
+{
+    BouncingParticlesStage.call(this, element, options);
+    this.context = this.element.getContext("2d");
+}
+
+BouncingCanvasParticlesStage.prototype = Object.create(BouncingParticlesStage.prototype);
+BouncingCanvasParticlesStage.prototype.constructor = BouncingCanvasParticlesStage;
+
+function BouncingCanvasParticlesAnimator(benchmark)
+{
+    BouncingParticlesAnimator.call(this, benchmark);
+    this._context = benchmark._stage.context;
+}
+
+BouncingCanvasParticlesAnimator.prototype = Object.create(BouncingParticlesAnimator.prototype);
+BouncingCanvasParticlesAnimator.prototype.constructor = BouncingCanvasParticlesAnimator;
+
+BouncingCanvasParticlesAnimator.prototype.animate = function()
+{
+    this._context.clearRect(0, 0, this._benchmark._stage.size.x, this._benchmark._stage.size.y);
+    return BouncingParticlesAnimator.prototype.animate.call(this);
+}
+
+function BouncingCanvasParticlesBenchmark(suite, test, options, recordTable, progressBar)
+{
+    StageBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+BouncingCanvasParticlesBenchmark.prototype = Object.create(StageBenchmark.prototype);
+BouncingCanvasParticlesBenchmark.prototype.constructor = BouncingCanvasParticlesBenchmark;
+
+BouncingCanvasParticlesBenchmark.prototype.createAnimator = function()
+{
+    return new BouncingCanvasParticlesAnimator(this);
+}
\ No newline at end of file
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-canvas-shapes.js b/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-canvas-shapes.js
new file mode 100644 (file)
index 0000000..f22a757
--- /dev/null
@@ -0,0 +1,90 @@
+function BouncingCanvasShape(stage)
+{
+    BouncingCanvasParticle.call(this, stage);
+    this._fill = stage.fill;
+    this._shape = stage.shape;
+    this._color0 = stage.randomColor();
+    this._color1 = stage.randomColor();    
+}
+
+BouncingCanvasShape.prototype = Object.create(BouncingCanvasParticle.prototype);
+BouncingCanvasShape.prototype.constructor = BouncingCanvasShape;
+
+BouncingCanvasShape.prototype._applyFill = function()
+{
+    switch (this._fill) {
+    case "gradient":
+        var gradient = this._context.createLinearGradient(0, 0, this._size.width, 0);
+        gradient.addColorStop(0, this._color0);
+        gradient.addColorStop(1, this._color1); 
+        this._context.fillStyle = gradient;
+        break;
+    
+    case "solid":
+    default:
+        this._context.fillStyle = this._color0;
+        break;
+    }
+}
+
+BouncingCanvasShape.prototype._drawShape = function()
+{
+    this._context.beginPath();
+
+    switch (this._shape) {
+    case "rect":
+        this._context.rect(0, 0, this._size.width, this._size.height);
+        break;
+    
+    case "circle":
+    default:
+        var center = this._size.center;
+        var radius = Math.min(this._size.x, this._size.y) / 2;
+        this._context.arc(center.x, center.y, radius, 0, Math.PI * 2, true);
+        break;
+    }
+
+    this._context.fill();
+}
+
+BouncingCanvasShape.prototype._draw = function()
+{
+    this._context.save();
+        this._applyFill();
+        this._applyRotation();
+        this._applyClipping();
+        this._drawShape();
+    this._context.restore();
+}
+
+function BouncingCanvasShapesStage(element, options)
+{
+    BouncingCanvasParticlesStage.call(this, element, options);
+    this.parseShapeParamters(options);
+}
+
+BouncingCanvasShapesStage.prototype = Object.create(BouncingCanvasParticlesStage.prototype);
+BouncingCanvasShapesStage.prototype.constructor = BouncingCanvasShapesStage;
+
+BouncingCanvasShapesStage.prototype.createParticle = function()
+{
+    return new BouncingCanvasShape(this);
+}
+
+function BouncingCanvasShapesBenchmark(suite, test, options, recordTable, progressBar)
+{
+    BouncingCanvasParticlesBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+BouncingCanvasShapesBenchmark.prototype = Object.create(BouncingCanvasParticlesBenchmark.prototype);
+BouncingCanvasShapesBenchmark.prototype.constructor = BouncingCanvasShapesBenchmark;
+
+BouncingCanvasShapesBenchmark.prototype.createStage = function(element)
+{
+    return new BouncingCanvasShapesStage(element, this._options);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    return new BouncingCanvasShapesBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-css-images.js b/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-css-images.js
new file mode 100644 (file)
index 0000000..96f0e59
--- /dev/null
@@ -0,0 +1,63 @@
+function BouncingCssImage(stage)
+{
+    BouncingParticle.call(this, stage);
+
+    this.element = document.createElement("img");
+    this.element.style.width = this._size.x + "px";
+    this.element.style.height = this._size.y + "px";
+    this.element.setAttribute("src", stage.imageSrc);
+    
+    stage.element.appendChild(this.element);
+    this._move();
+}
+
+BouncingCssImage.prototype = Object.create(BouncingParticle.prototype);
+BouncingCssImage.prototype.constructor = BouncingCssImage;
+
+BouncingCssImage.prototype._move = function()
+{
+    this.element.style.transform = "translate(" + this._position.x + "px," + this._position.y + "px) " + this._rotater.rotateZ();
+}
+    
+BouncingCssImage.prototype.animate = function(timeDelta)
+{
+    BouncingParticle.prototype.animate.call(this, timeDelta);
+    this._move();
+}
+
+function BouncingCssImagesStage(element, options)
+{
+    BouncingParticlesStage.call(this, element, options);
+    this.imageSrc = options["imageSrc"] || "../resources/yin-yang.svg";
+}
+
+BouncingCssImagesStage.prototype = Object.create(BouncingParticlesStage.prototype);
+BouncingCssImagesStage.prototype.constructor = BouncingCssImagesStage;
+
+BouncingCssImagesStage.prototype.createParticle = function()
+{
+    return new BouncingCssImage(this);
+}
+
+BouncingCssImagesStage.prototype.particleWillBeRemoved = function(particle)
+{
+    particle.element.remove();
+}
+
+function BouncingCssImagesBenchmark(suite, test, options, recordTable, progressBar)
+{
+    BouncingParticlesBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+BouncingCssImagesBenchmark.prototype = Object.create(BouncingParticlesBenchmark.prototype);
+BouncingCssImagesBenchmark.prototype.constructor = BouncingCssImagesBenchmark;
+
+BouncingCssImagesBenchmark.prototype.createStage = function(element)
+{
+    return new BouncingCssImagesStage(element, this._options);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    return new BouncingCssImagesBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-css-shapes.js b/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-css-shapes.js
new file mode 100644 (file)
index 0000000..9dffe65
--- /dev/null
@@ -0,0 +1,81 @@
+function BouncingCssShape(stage)
+{
+    BouncingParticle.call(this, stage);
+
+    this.element = this._createSpan(stage);
+        
+    switch (stage.fill) {
+    case "solid":
+    default:
+        this.element.style.backgroundColor = stage.randomColor();
+        break;
+        
+    case "gradient":
+        this.element.style.background = "linear-gradient(" + stage.randomColor() + ", " + stage.randomColor() + ")";
+        break;
+    }
+
+    this._move();
+}
+
+BouncingCssShape.prototype = Object.create(BouncingParticle.prototype);
+BouncingCssShape.prototype.constructor = BouncingCssShape;
+
+BouncingCssShape.prototype._createSpan = function(stage)
+{
+    var span = document.createElement("span");
+    span.className = stage.shape + " " + stage.clip;
+    span.style.width = this._size.x + "px";
+    span.style.height = this._size.y + "px";
+    stage.element.appendChild(span);
+    return span;
+}
+
+BouncingCssShape.prototype._move = function()
+{
+    this.element.style.transform = "translate(" + this._position.x + "px," + this._position.y + "px)" + this._rotater.rotateZ();
+}
+    
+BouncingCssShape.prototype.animate = function(timeDelta)
+{
+    BouncingParticle.prototype.animate.call(this, timeDelta);
+    this._rotater.next(timeDelta);    
+    this._move();
+}
+
+function BouncingCssShapesStage(element, options)
+{
+    BouncingParticlesStage.call(this, element, options);
+    this.parseShapeParamters(options);
+}
+
+BouncingCssShapesStage.prototype = Object.create(BouncingParticlesStage.prototype);
+BouncingCssShapesStage.prototype.constructor = BouncingCssShapesStage;
+
+BouncingCssShapesStage.prototype.createParticle = function()
+{
+    return new BouncingCssShape(this);
+}
+
+BouncingCssShapesStage.prototype.particleWillBeRemoved = function(particle)
+{
+    particle.element.remove();
+}
+
+function BouncingCssShapesBenchmark(suite, test, options, recordTable, progressBar)
+{
+    BouncingParticlesBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+BouncingCssShapesBenchmark.prototype = Object.create(BouncingParticlesBenchmark.prototype);
+BouncingCssShapesBenchmark.prototype.constructor = BouncingCssShapesBenchmark;
+
+BouncingCssShapesBenchmark.prototype.createStage = function(element)
+{
+    return new BouncingCssShapesStage(element, this._options);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    return new BouncingCssShapesBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-particles.js b/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-particles.js
new file mode 100644 (file)
index 0000000..68c2f38
--- /dev/null
@@ -0,0 +1,132 @@
+function BouncingParticle(stage)
+{
+    this._stageSize = stage.size;
+    this._size = stage.particleSize;
+    
+    this._position = stage.randomPosition(stage.size.subtract(stage.particleSize));
+    this._angle = stage.randomAngle();
+    this._velocity = stage.randomVelocity(stage.maxVelocity);
+    this._rotater = stage.randomRotater();
+}
+
+BouncingParticle.prototype =
+{
+    get center()
+    {
+        return this._position.add(this._size.center);
+    },
+    
+    animate: function(timeDelta)
+    {
+        this._position = this._position.move(this._angle, this._velocity, timeDelta);
+        this._rotater.next(timeDelta);
+
+        // If particle is going to move off right side
+        if (this._position.x + this._size.x > this._stageSize.x) {
+            // If direction is East-South
+            if (this._angle >= 0 && this._angle < Math.PI / 2)
+                this._angle = Math.PI - this._angle;
+            // If angle is East-North
+            else if (this._angle > Math.PI / 2 * 3)
+                this._angle = this._angle - (this._angle - Math.PI / 2 * 3) * 2;
+        }
+        
+        // If particle is going to move off left side
+        if (this._position.x < 0) {
+            // If angle is West-South
+            if (this._angle > Math.PI / 2 && this._angle < Math.PI)
+                this._angle = Math.PI - this._angle;
+            // If angle is West-North
+            else if (this._angle > Math.PI && this._angle < Math.PI / 2 * 3)
+                this._angle = this._angle + (Math.PI / 2 * 3 - this._angle) * 2;
+        }
+
+        // If particle is going to move off bottom side
+        if (this._position.y + this._size.y > this._stageSize.y) {
+            // If direction is South
+            if (this._angle > 0 && this._angle < Math.PI)
+                this._angle = Math.PI * 2 - this._angle;
+        }
+
+        // If particle is going to move off top side
+        if (this._position.y < 0) {
+            // If direction is North
+            if (this._angle > Math.PI && this._angle < Math.PI * 2)
+                this._angle = this._angle - (this._angle - Math.PI) * 2;
+        }
+    }
+}
+
+function BouncingParticlesAnimator(benchmark)
+{
+    StageAnimator.call(this, benchmark);
+};
+
+BouncingParticlesAnimator.prototype = Object.create(StageAnimator.prototype);
+BouncingParticlesAnimator.prototype.constructor = BouncingParticlesAnimator;
+
+function BouncingParticlesStage(element, options)
+{
+    Stage.call(this, element);
+    
+    this.particleSize = new Point(parseInt(options["particleWidth"]) || 10, parseInt(options["particleHeight"]) || 10);
+    this._particles = [];
+
+    this.maxVelocity = parseInt(options["maxVelocity"]) || 500;
+    this.maxVelocity = Math.max(this.maxVelocity, 100);
+}
+
+BouncingParticlesStage.prototype = Object.create(Stage.prototype);
+BouncingParticlesStage.prototype.constructor = BouncingParticlesStage;
+
+BouncingParticlesStage.prototype.parseShapeParamters = function(options)
+{
+    this.shape = options["shape"] || "circle";
+    this.fill = options["fill"] || "solid";
+    this.clip = options["clip"] || "";
+}
+
+BouncingParticlesStage.prototype.animate = function(timeDelta)
+{
+    this._particles.forEach(function(particle) {
+        particle.animate(timeDelta);
+    });
+}
+    
+BouncingParticlesStage.prototype.tune = function(count)
+{
+    count = count > 0 ? Math.floor(count) : Math.ceil(count);
+
+    if (count == 0)
+        return this._particles.length;
+
+    if (count > 0) {
+        console.assert(typeof(this.createParticle) == "function");
+        for (var i = 0; i < count; ++i)
+            this._particles.push(this.createParticle());
+        return this._particles.length;
+    }
+
+    count = Math.min(-count, this._particles.length);
+        
+    if (typeof(this.particleWillBeRemoved) == "function") {
+        for (var i = 0; i < count; ++i)
+            this.particleWillBeRemoved(this._particles[this._particles.length - 1 - i]);
+    }
+
+    this._particles.splice(-count, count);
+    return this._particles.length;
+}
+
+function BouncingParticlesBenchmark(suite, test, options, recordTable, progressBar)
+{
+    StageBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+BouncingParticlesBenchmark.prototype = Object.create(StageBenchmark.prototype);
+BouncingParticlesBenchmark.prototype.constructor = BouncingParticlesBenchmark;
+
+BouncingParticlesBenchmark.prototype.createAnimator = function()
+{
+    return new BouncingParticlesAnimator(this);
+}
\ No newline at end of file
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-images.js b/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-images.js
new file mode 100644 (file)
index 0000000..cfe2c40
--- /dev/null
@@ -0,0 +1,45 @@
+function BouncingSvgImage(stage)
+{
+    BouncingSvgParticle.call(this, stage);
+    this._shape = "image";
+    
+    var attrs = { x: 0, y: 0, width: this._size.x, height: this._size.y };
+    var xlinkAttrs = { href: stage.imageSrc };
+    this.element = Utilities.createSvgElement("image", attrs, xlinkAttrs, stage.element);
+    this._move();
+}
+
+BouncingSvgImage.prototype = Object.create(BouncingSvgParticle.prototype);
+BouncingSvgImage.prototype.constructor = BouncingSvgImage;
+
+function BouncingSvgImagesStage(element, options)
+{
+    BouncingParticlesStage.call(this, element, options);
+    this.imageSrc = options["imageSrc"] || "resources/yin-yang.svg";
+}
+
+BouncingSvgImagesStage.prototype = Object.create(BouncingSvgParticlesStage.prototype);
+BouncingSvgImagesStage.prototype.constructor = BouncingSvgImagesStage;
+
+BouncingSvgImagesStage.prototype.createParticle = function()
+{
+    return new BouncingSvgImage(this);
+}
+
+function BouncingSvgImagesBenchmark(suite, test, options, recordTable, progressBar)
+{
+    BouncingParticlesBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+BouncingSvgImagesBenchmark.prototype = Object.create(BouncingParticlesBenchmark.prototype);
+BouncingSvgImagesBenchmark.prototype.constructor = BouncingSvgImagesBenchmark;
+
+BouncingSvgImagesBenchmark.prototype.createStage = function(element)
+{
+    return new BouncingSvgImagesStage(element, this._options);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    return new BouncingSvgImagesBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-particles.js b/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-particles.js
new file mode 100644 (file)
index 0000000..2d29bf4
--- /dev/null
@@ -0,0 +1,68 @@
+function BouncingSvgParticle(stage)
+{
+    BouncingParticle.call(this, stage);
+}
+
+BouncingSvgParticle.prototype = Object.create(BouncingParticle.prototype);
+BouncingSvgParticle.prototype.constructor = BouncingParticle;
+
+BouncingSvgParticle.prototype._applyClipping = function(stage)
+{
+    if (stage.clip != "star")
+        return;
+        
+    stage.ensureClipStarIsCreated();
+    this.element.setAttribute("clip-path", "url(#star-clip)");
+}
+
+BouncingSvgParticle.prototype._move = function()
+{
+    var transform = "translate(" + this._position.x + ", " + this._position.y + ")";
+    if (this._shape != "circle")
+        transform += this._rotater.rotate(this._size.center);
+    this.element.setAttribute("transform", transform);
+}
+
+BouncingSvgParticle.prototype.animate = function(timeDelta)
+{
+    BouncingParticle.prototype.animate.call(this, timeDelta);
+    this._move();
+}
+
+function BouncingSvgParticlesStage(element, options)
+{
+    BouncingParticlesStage.call(this, element, options);
+}
+
+BouncingSvgParticlesStage.prototype = Object.create(BouncingParticlesStage.prototype);
+BouncingSvgParticlesStage.prototype.constructor = BouncingSvgParticlesStage;
+
+BouncingSvgParticlesStage.prototype._createDefs = function()
+{
+    return Utilities.createSvgElement("defs", {}, {}, this.element);
+}
+                                                               
+BouncingSvgParticlesStage.prototype._ensureDefsIsCreated = function()
+{
+    return this.element.querySelector("defs") || this._createDefs();
+}
+
+BouncingSvgParticlesStage.prototype._createClipStar = function()
+{
+    var attrs = { id: "star-clip", clipPathUnits: "objectBoundingBox" };
+    var clipPath  = Utilities.createSvgElement("clipPath", attrs, {}, this._ensureDefsIsCreated());
+
+    attrs = { d: "M.50,0L.38,.38L0,.38L.30,.60L.18,1L.50,.75L.82,1L.70,.60L1,.38L.62,.38z" };
+    Utilities.createSvgElement("path", attrs, {}, clipPath);
+    return clipPath;
+}
+
+BouncingSvgParticlesStage.prototype.ensureClipStarIsCreated = function()
+{
+    return this.element.querySelector("#star-clip") || this._createClipStar();
+}
+
+BouncingSvgParticlesStage.prototype.particleWillBeRemoved = function(particle)
+{
+    particle.element.remove();
+}
diff --git a/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-shapes.js b/PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-shapes.js
new file mode 100644 (file)
index 0000000..1980110
--- /dev/null
@@ -0,0 +1,104 @@
+function BouncingSvgShape(stage)
+{
+    BouncingSvgParticle.call(this, stage);
+    this._shape = stage.shape;
+    this._fill = stage.fill;
+    
+    this._createShape(stage);
+    this._applyClipping(stage);
+    this._applyFill(stage);
+
+    this._move();    
+}
+
+BouncingSvgShape.prototype = Object.create(BouncingSvgParticle.prototype);
+BouncingSvgShape.prototype.constructor = BouncingSvgShape;
+
+BouncingSvgShape.prototype._createShape = function(stage)
+{
+    switch (this._shape) {
+    case "rect":
+        var attrs = { x: 0, y: 0, width: this._size.x, height: this._size.y };
+        this.element = Utilities.createSvgElement("rect", attrs, {}, stage.element);
+        break;
+    
+    case "circle":
+    default:
+        var attrs = { cx: this._size.x / 2, cy: this._size.y / 2, r: Math.min(this._size.x, this._size.y) / 2 };
+        this.element = Utilities.createSvgElement("circle", attrs, {}, stage.element);
+        break;        
+    }
+}
+
+BouncingSvgShape.prototype._applyFill = function(stage)
+{
+    switch (this._fill) {
+    case "gradient":
+        var gradient = stage.createGradient(2);
+        this.element.setAttribute("fill", "url(#" + gradient.getAttribute("id") + ")");
+        break;
+    
+    case "solid":
+    default:
+        this.element.setAttribute("fill", stage.randomColor());
+        break;
+    }
+}
+
+function BouncingSvgShapesStage(element, options)
+{
+    BouncingSvgParticlesStage.call(this, element, options);
+    this.parseShapeParamters(options);
+    this._gradientsCount = 0;
+}
+
+BouncingSvgShapesStage.prototype = Object.create(BouncingSvgParticlesStage.prototype);
+BouncingSvgShapesStage.prototype.constructor = BouncingSvgShapesStage;
+                                                               
+BouncingSvgShapesStage.prototype.createGradient = function(stops)
+{
+    var attrs = { id: "gadient-" + ++this._gradientsCount };
+    var gradient = Utilities.createSvgElement("linearGradient", attrs, {}, this._ensureDefsIsCreated());    
+    
+    for (var i = 0; i < stops; ++i) {
+        attrs = { offset: i * 100 / stops + "%", 'stop-color': this.randomColor() };
+        Utilities.createSvgElement("stop", attrs, {}, gradient);
+    }
+
+    return gradient;
+}
+
+BouncingSvgShapesStage.prototype.createParticle = function()
+{
+    return new BouncingSvgShape(this);
+}
+
+BouncingSvgShapesStage.prototype.particleWillBeRemoved = function(particle)
+{
+    BouncingSvgParticlesStage.prototype.particleWillBeRemoved.call(this, particle);
+
+    var fill = particle.element.getAttribute("fill");
+    if (fill.indexOf("url(#") != 0)
+        return;
+        
+    var gradient = this.element.querySelector(fill.substring(4, fill.length - 1));
+    this._ensureDefsIsCreated().removeChild(gradient);
+}
+
+function BouncingSvgShapesBenchmark(suite, test, options, recordTable, progressBar)
+{
+    BouncingParticlesBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+BouncingSvgShapesBenchmark.prototype = Object.create(BouncingParticlesBenchmark.prototype);
+BouncingSvgShapesBenchmark.prototype.constructor = BouncingSvgShapesBenchmark;
+
+BouncingSvgShapesBenchmark.prototype.createStage = function(element)
+{
+    return new BouncingSvgShapesStage(element, this._options);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    return new BouncingSvgShapesBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/examples/canvas-electrons.html b/PerformanceTests/Animometer/tests/examples/canvas-electrons.html
new file mode 100644 (file)
index 0000000..22c20a6
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>
+    <script src="resources/canvas-electrons.js"></script>
+</head>
+<body>
+    <canvas id="stage"></canvas>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/examples/canvas-stars.html b/PerformanceTests/Animometer/tests/examples/canvas-stars.html
new file mode 100644 (file)
index 0000000..abf6ea7
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>
+    <script src="resources/canvas-stars.js"></script>
+</head>
+<body>
+    <canvas id="stage"></canvas>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/examples/resources/canvas-electrons.js b/PerformanceTests/Animometer/tests/examples/resources/canvas-electrons.js
new file mode 100644 (file)
index 0000000..5e12e9f
--- /dev/null
@@ -0,0 +1,134 @@
+function CanvasElectron(stage)
+{
+    this._context = stage.context;
+    this._stageSize = stage.size;
+
+    var minSide = Math.min(this._stageSize.width, this._stageSize.height);
+    var radiusX = stage.random(minSide / 8, 7 * minSide / 16);
+    var radiusY = stage.random(minSide / 8, 3 * radiusX / 4);
+    this._orbitRadiuses = new Point(radiusX, radiusY);
+    this._radius = stage.random(5, 15);
+    this._direction = stage.randomInt(0, 2);
+    this._angle = stage.randomInt(0, 360);
+    this._color = stage.randomColor();
+    this._rotater = stage.randomRotater();
+    this._rotater.next(stage.random(0, this._rotater.interval));
+}
+
+CanvasElectron.prototype._draw = function()
+{
+    // Calculate the position of the object on the ellipse.
+    var angle = this._direction ? this._rotater.degree() : 360 - this._rotater.degree();
+    var position = this._stageSize.center.subtract(Point.pointOnEllipse(angle, this._orbitRadiuses));
+
+    this._context.save();    
+        this._context.translate(this._stageSize.center.x, this._stageSize.center.y);
+        this._context.rotate(this._angle * Math.PI / 180);
+        this._context.translate(-this._stageSize.center.x, -this._stageSize.center.y);
+
+        // Set the stroke and the fill colors
+        this._context.strokeStyle = "rgba(192, 192, 192, 0.9)";
+        this._context.fillStyle = this._color;
+
+        // Draw the orbit of the object.
+        this._context.beginPath();
+        this._context.ellipse(this._stageSize.center.x, this._stageSize.center.y, this._orbitRadiuses.x, this._orbitRadiuses.y, 0, 0, 2 * Math.PI);
+        this._context.stroke();
+    
+        // Draw the object.
+        this._context.beginPath();
+        this._context.arc(position.x, position.y, this._radius, 0, Math.PI * 2, true);
+        this._context.fill();
+    this._context.restore();
+}
+
+CanvasElectron.prototype.animate = function(timeDelta)
+{
+    this._rotater.next(timeDelta / 100);
+    this._draw();
+}
+
+function CanvasElectronsStage(element, options)
+{
+    Stage.call(this, element, options);
+    this.context = this.element.getContext("2d");
+    this._electrons = [];
+}
+
+CanvasElectronsStage.prototype = Object.create(Stage.prototype);
+CanvasElectronsStage.prototype.constructor = CanvasElectronsStage;
+
+CanvasElectronsStage.prototype.tune = function(count)
+{
+    count = count > 0 ? Math.floor(count) : Math.ceil(count);
+
+    if (count == 0)
+        return this._electrons.length;
+
+    if (count > 0) {
+        for (var i = 0; i < count; ++i)
+            this._electrons.push(new CanvasElectron(this));
+        return this._electrons.length;
+    }
+
+    count = Math.min(-count, this._electrons.length);
+    this._electrons.splice(-count, count);
+    
+    return this._electrons.length;
+}
+
+CanvasElectronsStage.prototype.animate = function(timeDelta)
+{
+    this._electrons.forEach(function(electron) {
+        electron.animate(timeDelta);
+    });
+}
+
+function CanvasElectronsAnimator(benchmark)
+{
+    Animator.call(this, benchmark);
+    this._context = benchmark._stage.context;
+}
+
+CanvasElectronsAnimator.prototype = Object.create(StageAnimator.prototype);
+CanvasElectronsAnimator.prototype.constructor = CanvasElectronsAnimator;
+
+CanvasElectronsAnimator.prototype.animate = function()
+{
+    this._context.clearRect(0, 0, this._benchmark._stage.size.x, this._benchmark._stage.size.y);
+
+    // Draw a big star in the middle.
+    this._context.fillStyle = "orange";    
+    this._context.beginPath();
+    this._context.arc(this._benchmark._stage.size.center.x, this._benchmark._stage.size.center.y, 50, 0, Math.PI * 2, true);
+    this._context.fill();
+
+    return StageAnimator.prototype.animate.call(this);
+}
+
+function CanvasElectronsBenchmark(suite, test, options, recordTable, progressBar)
+{
+    StageBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+CanvasElectronsBenchmark.prototype = Object.create(StageBenchmark.prototype);
+CanvasElectronsBenchmark.prototype.constructor = CanvasElectronsBenchmark;
+
+CanvasElectronsBenchmark.prototype.createStage = function(element)
+{
+    // Attach the stage to the benchmark.
+    return new CanvasElectronsStage(element, this._options);
+}
+
+CanvasElectronsBenchmark.prototype.createAnimator = function()
+{
+    // Attach the animator to the benchmark.
+    return new CanvasElectronsAnimator(this);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    // This function is called from the test harness which starts the
+    // test by creating your benchmark object.
+    return new CanvasElectronsBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/examples/resources/canvas-stars.js b/PerformanceTests/Animometer/tests/examples/resources/canvas-stars.js
new file mode 100644 (file)
index 0000000..343fa34
--- /dev/null
@@ -0,0 +1,131 @@
+function CanvasStar(stage)
+{
+    this._context = stage.context;
+    this._stageSize = stage.size;
+
+    this._size = stage.randomSquareSize(5, 20);
+    this._center = stage.randomPosition(stage.size.subtract(this._size)).add(this._size.center);
+    this._rotateX = 0;
+    this._rotateDeltaX = stage.random(0.3, 0.7);
+}
+
+CanvasStar.prototype._draw = function()
+{
+    this._context.save();
+    this._context.translate(this._center.x, this._center.y);
+    
+    this._context.fillStyle = 'yellow';
+    this._context.strokeStyle = 'white';
+
+    this._context.lineWidth = 1;
+    
+    this._context.beginPath();
+    this._context.moveTo(                                     0, -this._size.y /  2);
+    this._context.lineTo(+this._size.x / 20 - this._rotateX / 5, -this._size.y / 10);
+    this._context.lineTo(+this._size.x / 4  - this._rotateX,                      0);
+    this._context.lineTo(+this._size.x / 20 - this._rotateX / 5, +this._size.y / 10);
+    this._context.lineTo(                                     0, +this._size.y /  2);
+    this._context.lineTo(-this._size.x / 20 + this._rotateX / 5, +this._size.y / 10);
+    this._context.lineTo(-this._size.x /  4 + this._rotateX,                      0);
+    this._context.lineTo(-this._size.x / 20 + this._rotateX / 5, -this._size.y / 10);
+    this._context.lineTo(                                     0, -this._size.y /  2);
+    
+    this._context.fill();
+    this._context.stroke();
+    this._context.closePath();
+    this._context.restore();
+}
+
+CanvasStar.prototype.animate = function(timeDelta)
+{
+    this._rotateX += this._rotateDeltaX;
+    
+    if (this._rotateX > this._size.x / 4 || this._rotateX < -this._size.x / 4) {
+        this._rotateDeltaX = -this._rotateDeltaX;
+        this._rotateX += this._rotateDeltaX;
+    }
+
+   this._draw();
+}
+
+function CanvasStarsStage(element, options)
+{
+   Stage.call(this, element, options);
+   this.context = this.element.getContext("2d");
+
+    this._objects = [];
+}
+
+CanvasStarsStage.prototype = Object.create(Stage.prototype);
+CanvasStarsStage.prototype.constructor = CanvasStarsStage;
+
+CanvasStarsStage.prototype.tune = function(count)
+{
+    count = count > 0 ? Math.floor(count) : Math.ceil(count);
+
+    if (count == 0)
+        return this._objects.length;
+
+    if (count > 0) {
+        for (var i = 0; i < count; ++i)
+            this._objects.push(new CanvasStar(this));
+        return this._objects.length;
+    }
+
+    count = Math.min(-count, this._objects.length);
+    this._objects.splice(-count, count);
+
+    return this._objects.length;
+}
+
+CanvasStarsStage.prototype.animate = function(timeDelta)
+{
+    this._objects.forEach(function(object) {
+        object.animate(timeDelta);
+    });
+}
+
+function CanvasStarsAnimator(benchmark)
+{
+   Animator.call(this, benchmark);
+   this._context = benchmark._stage.context;
+}
+
+CanvasStarsAnimator.prototype = Object.create(StageAnimator.prototype);
+CanvasStarsAnimator.prototype.constructor = CanvasStarsAnimator;
+
+CanvasStarsAnimator.prototype.animate = function()
+{
+    this._context.beginPath();
+    this._context.fillStyle = 'black';
+    this._context.rect(0, 0, this._benchmark._stage.size.width, this._benchmark._stage.size.height);
+    this._context.fill();
+   return StageAnimator.prototype.animate.call(this);
+}
+
+function CanvasStarsBenchmark(suite, test, options, recordTable, progressBar)
+{
+   StageBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+CanvasStarsBenchmark.prototype = Object.create(StageBenchmark.prototype);
+CanvasStarsBenchmark.prototype.constructor = CanvasStarsBenchmark;
+
+CanvasStarsBenchmark.prototype.createStage = function(element)
+{
+   // Attach the stage to the benchmark.
+   return new CanvasStarsStage(element, this._options);
+}
+
+CanvasStarsBenchmark.prototype.createAnimator = function()
+{
+   // Attach the animator to the benchmark.
+   return new CanvasStarsAnimator(this);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+   // This function is called from the test harness which starts the
+   // test by creating your benchmark object.
+   return new CanvasStarsBenchmark(suite, test, options, recordTable, progressBar);
+}
\ No newline at end of file
diff --git a/PerformanceTests/Animometer/tests/resources/main.js b/PerformanceTests/Animometer/tests/resources/main.js
new file mode 100644 (file)
index 0000000..1920f9b
--- /dev/null
@@ -0,0 +1,242 @@
+function BenchmarkState(testInterval)
+{
+    this._currentTimeOffset = 0;
+    this._stageInterval = testInterval / BenchmarkState.stages.FINISHED;
+}
+
+// The enum values and the messages should be in the same order
+BenchmarkState.stages = {
+    WARMING_UP: 0,
+    SAMPLING: 1,
+    FINISHED: 2,
+    messages: [ "Warming up", "Sampling", "Finished" ]
+}
+
+BenchmarkState.prototype =
+{
+    _timeOffset: function(stage)
+    {
+        return stage * this._stageInterval;
+    },
+    
+    _message: function(stage, timeOffset)
+    {
+        if (stage == BenchmarkState.stages.FINISHED)
+            return BenchmarkState.stages.messages[stage];
+
+        return BenchmarkState.stages.messages[stage] + "... ("
+            + Math.floor((timeOffset - this._timeOffset(stage)) / 1000) + "/"
+            + Math.floor((this._timeOffset(stage + 1) - this._timeOffset(stage)) / 1000) + ")";
+    },
+    
+    update: function(currentTimeOffset)
+    {
+        this._currentTimeOffset = currentTimeOffset;
+    },
+    
+    samplingTimeOffset: function()
+    {
+        return this._timeOffset(BenchmarkState.stages.SAMPLING);
+    },
+    
+    currentStage: function()
+    {
+        for (var stage = BenchmarkState.stages.WARMING_UP; stage < BenchmarkState.stages.FINISHED; ++stage) {
+            if (this._currentTimeOffset < this._timeOffset(stage + 1))
+                return stage;
+        }
+        return BenchmarkState.stages.FINISHED;
+    },
+    
+    currentMessage: function()
+    {
+        return this._message(this.currentStage(), this._currentTimeOffset);
+    },
+    
+    currentProgress: function()
+    {
+        return this._currentTimeOffset / this._timeOffset(BenchmarkState.stages.FINISHED);
+    }
+}
+
+function Animator(benchmark)
+{
+    this._benchmark = benchmark;
+    this._frameCount = 0;
+    this._dropFrameCount = 1;
+    this._measureFrameCount = 3; 
+    this._referenceTime = 0;
+    this._currentTimeOffset = 0;
+    this._estimator = new KalmanEstimator();
+}
+
+Animator.prototype =
+{
+    start: function()
+    {
+        this._intervalId = setInterval(this.animate.bind(this), 1);
+    },
+    
+    timeDelta: function()
+    {
+        return this._currentTimeOffset - this._startTimeOffset;
+    },
+    
+    animate: function()
+    {
+        var currentTime = performance.now();
+        
+        if (!this._referenceTime)
+            this._referenceTime = currentTime;
+        else
+            this._currentTimeOffset = currentTime - this._referenceTime;
+
+        if (!this._frameCount)
+            this._startTimeOffset = this._currentTimeOffset;
+
+        // Start measuring after dropping _dropFrameCount frames.
+        if (this._frameCount == this._dropFrameCount)
+            this._measureTimeOffset = this._currentTimeOffset;
+
+        ++this._frameCount;
+
+        // Drop _dropFrameCount frames and measure the average of _measureFrameCount frames.
+        if (this._frameCount < this._dropFrameCount + this._measureFrameCount)
+            return true;
+
+        // Get the average FPS of _measureFrameCount frames over measureTimeDelta.
+        var measureTimeDelta = this._currentTimeOffset - this._measureTimeOffset;
+        var currentFrameRate = Math.floor(1000 / (measureTimeDelta / this._measureFrameCount));
+         
+        // Use Kalman filter to get a more non-fluctuating frame rate.
+        if (this._benchmark.options.estimatedFrameRate)
+            currentFrameRate = this._estimator.estimate(measureTimeDelta, currentFrameRate);
+        
+        // Adjust the test to reach the desired FPS.
+        var result = this._benchmark.update(this._currentTimeOffset, this.timeDelta(), currentFrameRate);
+        
+        // Stop the animator if the benchmark has finished.
+        if (!result && typeof this._intervalId != "undefined")
+            clearInterval(this._intervalId);
+
+        // Start the next drop/measure cycle.
+        this._frameCount = 0;
+        
+        // result may stop the animator if requestAnimationFrame() has been used.
+        return result;
+    },
+    
+    animateLoop: function(timestamp)
+    {
+        if (this.animate())
+            requestAnimationFrame(this.animateLoop.bind(this));
+    }
+}
+
+function Benchmark(options)
+{
+    this._options = options;
+    this._method = this._options["method"] || "requestAnimationFrame";
+
+    this.options = options;
+    this._recordInterval = 200;    
+    this._isSampling = false;
+
+    var gain = parseInt(this._options["gain"]) || 1;
+    var lowValue = -parseInt(this._options["addLimit"]) || 1;
+    var highValue = parseInt(this._options["removeLimit"]) || 1;
+    
+    this._controller = new PIDController(gain, options.frameRate, lowValue, highValue);
+    this._sampler = new Sampler(2);
+    this._state = new BenchmarkState(this.options.testInterval);    
+}
+
+Benchmark.prototype =
+{
+    // Called from the load event listener or from this.run().
+    start: function()
+    {
+        if (this._method == "setInterval")
+            this._animator.start();
+        else
+            this._animator.animateLoop();
+    },
+    
+    // Called from the animator to adjust the complexity of the test.
+    update: function(currentTimeOffset, timeDelta, currentFrameRate)
+    {
+        this._state.update(currentTimeOffset);
+        
+        var stage = this._state.currentStage();
+        if (stage == BenchmarkState.stages.FINISHED) {
+            this.clear();
+            return false;
+        }
+
+        if (stage == BenchmarkState.stages.SAMPLING && !this._isSampling) {
+            this._sampler.startSampling(this._state.samplingTimeOffset());
+            this._isSampling = true;
+        }
+
+        var tuneValue = 0;
+        if (!(this._isSampling && this.options.fixTestComplexity)) {
+            // The relationship between frameRate and test complexity is inverse-proportional so we
+            // need to use the negative of PIDController.tune() to change the complexity of the test.
+            tuneValue = -this._controller.tune(currentFrameRate, timeDelta / 1000);
+        }
+
+        var currentComplexity = this.tune(tuneValue);
+        this.record(currentTimeOffset, currentComplexity, currentFrameRate);
+        return true;
+    },
+    
+    record: function(currentTimeOffset, currentComplexity, currentFrameRate)
+    {
+        this._sampler.sample(currentTimeOffset, [currentComplexity, currentFrameRate]);
+        
+        if (typeof this._recordTimeOffset == "undefined")
+            this._recordTimeOffset = currentTimeOffset;
+
+        var stage = this._state.currentStage();
+        if (stage != BenchmarkState.stages.FINISHED && currentTimeOffset < this._recordTimeOffset + this._recordInterval)
+            return;
+
+        this.showResults(this._state.currentMessage(), this._state.currentProgress());
+        this._recordTimeOffset = currentTimeOffset;
+    },
+    
+    run: function()
+    {
+        this.start();
+
+        var promise = new SimplePromise;
+        var self = this;
+        function resolveWhenFinished() {
+            if (typeof self._state != "undefined" && (self._state.currentStage() == BenchmarkState.stages.FINISHED))
+                return promise.resolve(self._sampler);
+            setTimeout(resolveWhenFinished.bind(self), 50);
+        }
+        
+        resolveWhenFinished();
+        return promise;
+    }
+}
+
+window.benchmarkClient = {};
+
+// This event listener runs the test if it is loaded outside the benchmark runner.
+window.addEventListener("load", function()
+{
+    if (window.self !== window.top)
+        return;
+    window.benchmark = window.benchmarkClient.create(null, null, 30000, 50, null, null);
+    window.benchmark.start();
+});
+
+// This function is called from the suite controller run-callback when running the benchmark runner.
+window.runBenchmark = function(suite, test, options, recordTable, progressBar)
+{
+    var mergedOptions = Utilities.mergeObjects(options, Utilities.parseParameters());
+    window.benchmark = window.benchmarkClient.create(suite, test, mergedOptions, recordTable, progressBar);
+    return window.benchmark.run();
+}
diff --git a/PerformanceTests/Animometer/tests/resources/math.js b/PerformanceTests/Animometer/tests/resources/math.js
new file mode 100644 (file)
index 0000000..c13833b
--- /dev/null
@@ -0,0 +1,274 @@
+var Matrix =
+{
+    init: function(m, n, v)
+    {
+        return Array(m * n).fill(v);
+    },
+    
+    zeros: function(m, n)
+    {
+        return Matrix.init(m, n, 0);
+    },
+    
+    ones: function(m, n)
+    {
+        return Matrix.init(m, n, 1);
+    },
+    
+    identity: function(n)
+    {
+        var out = new Matrix.zeros(n, n);
+        for (var i = 0; i < n; ++i)
+            out[i * n + i] = 1;
+        return out;
+    },
+    
+    str: function(A, n, m)
+    {
+        var out = (m > 1 && n > 1 ? "Matrix[" + n + ", " + m : "Vector[" + m * n) + "] = [";
+        for (var i = 0; i < n * m; ++i) {
+            out += A[i];
+            if (i < n * m - 1)
+                out += ", ";
+        }       
+        return out + "]";
+    },
+    
+    pos: function(m, i, j)
+    {
+        return m * i + j;
+    },
+    
+    add: function(A, B, n, m)
+    {
+        var out = Matrix.zeros(n, m);
+        for (var i = 0; i < n * m; ++i)
+            out[i] = A[i] + B[i];
+        return out;
+    },
+    
+    subtract: function(A, B, n, m)
+    {
+        var out = Matrix.zeros(n, m);
+        for (var i = 0; i < n * m; ++i)
+            out[i] = A[i] - B[i];
+        return out;
+    },
+    
+    scale: function(s, A, n, m)
+    {
+        var out = Matrix.zeros(n, m);
+        for (var i = 0; i < n * m; ++i)
+            out[i] = s * A[i];
+        return out;
+    },
+    
+    transpose: function(A, n, m)
+    {
+        var out = Matrix.zeros(m, n);
+        for (var i = 0; i < n; ++i) {
+            for (var j = 0; j < m; ++j)
+                out[Matrix.pos(n, i, j)] = A[Matrix.pos(m, j, i)];
+        }       
+        return out;
+    },
+    
+    multiply: function(A, B, n, m, p)
+    {
+        var out = Matrix.zeros(n, p);
+        for (var i = 0; i < n; ++i) {
+            for (var j = 0; j < p; ++j) {
+                for (var k = 0; k < m; ++k) {
+                    out[Matrix.pos(p, i, j)] += A[Matrix.pos(m, i, k)] * B[Matrix.pos(p, k, j)];
+                }
+            }
+        }
+        return out;
+    }
+}
+
+var Vector3 =
+{
+    zeros: function()
+    {
+        return Matrix.zeros(1, 3);
+    },
+    
+    ones: function()
+    {
+        return Matrix.ones(1, 3);
+    },
+    
+    str: function(v)
+    {
+        return Matrix.str(v, 1, 3);
+    },
+    
+    add: function(v, w)
+    {
+        return Matrix.add(v, w, 1, 3);
+    },
+    
+    subtract: function(v, w)
+    {
+        return Matrix.subtract(v, w, 1, 3);
+    },
+    
+    scale: function(s, v)
+    {
+        return Matrix.scale(s, v, 1, 3);
+    },
+    
+    multiplyMatrix3: function(v, A)
+    {
+        return Matrix.multiply(v, A, 1, 3, 3);
+    },
+    
+    multiplyVector3: function(v, w)
+    {
+        var out = 0;
+        for (var i = 0; i < 3; ++i)
+            out += v[i] * w[i];
+        return out;
+    }
+}
+
+var Matrix3 =
+{
+    zeros: function()
+    {
+        return Matrix.zeros(3, 3);
+    },
+    
+    identity: function()
+    {
+        return Matrix.identity(3, 3);
+    },
+    
+    str: function(A)
+    {
+        return Matrix.str(A, 3, 3);
+    },
+    
+    pos: function(i, j)
+    {
+        return Matrix.pos(3, i, j);
+    },
+    
+    add: function(A, B)
+    {
+        return Matrix.add(A, B, 3, 3);
+    },
+    
+    subtract: function(A, B)
+    {
+        return Matrix.subtract(A, B, 3, 3);
+    },
+    
+    scale: function(s, A)
+    {
+        return Matrix.scale(s, A, 3, 3);
+    },
+    
+    transpose: function(A)
+    {
+        return Matrix.transpose(A, 3, 3);
+    },
+    
+    multiplyMatrix3: function(A, B)
+    {
+        return Matrix.multiply(A, B, 3, 3, 3);
+    },
+    
+    multiplyVector3: function(A, v)
+    {
+        return Matrix.multiply(A, v, 3, 3, 1);
+    }
+}
+
+function PIDController(K, ysp, ulow, uhigh)
+{
+    this._ysp = ysp;
+
+    this._Td = 5;
+    this._Ti = 15;
+
+    this._Kp = K;
+    this._Kd = K / this._Td;
+    this._Ki = K / this._Ti;
+
+    this._eold = 0;
+    this._I = 0;
+}
+
+PIDController.prototype =
+{
+    _sat: function(v, low, high)
+    {
+        return v < low ? low : (v > high ? high : v);
+    },
+    
+    tune: function(y, h)
+    {
+        // Current error.
+        var e = this._ysp - y;
+
+        // Proportional term.
+        var P = this._Kp * e;
+        
+        // Derivative term is the slope of the curve at the current time.
+        var D = this._Kd * (e - this._eold) / h;
+
+        // Integral term is the area under the curve starting from the begining till the current time.
+        this._I += this._Ki * ((e + this._eold) / 2) * h;
+
+        // pid controller value.
+        var v = P + D + this._I;
+        
+        // Avoid spikes by applying actuator saturation.
+        var u = this._sat(v, this._ulow, this._uhigh);        
+
+        this._eold = e;
+        return u;
+    }
+}
+
+function KalmanEstimator()
+{
+    this._matA = Matrix3.identity();
+    
+    this._vecH = Vector3.zeros();
+    this._vecH[0] = 1;
+    
+    this._matQ = Matrix3.identity();
+    this._R = 1000;
+    
+    this._vecX_est = Vector3.ones();
+    this._matP_est = Matrix3.zeros();
+}
+
+KalmanEstimator.prototype =
+{
+    estimate: function(timeDelta, current)
+    {
+        // Update the transition matrix.
+        this._matA[Matrix3.pos(0, 2)] = 1;
+
+        // Predicted state and covariance.
+        var vecX_prd = Matrix3.multiplyVector3(this._matA, this._vecX_est);
+        var matP_prd = Matrix3.add(Matrix3.multiplyMatrix3(Matrix3.multiplyMatrix3(this._matA, this._matP_est), Matrix3.transpose(this._matA)), this._matQ);
+
+        // Estimation.
+        var vecB = Vector3.multiplyMatrix3(this._vecH, Matrix3.transpose(matP_prd));
+        var S = Vector3.multiplyVector3(vecB, this._vecH) + this._R;
+
+        var vecGain = Vector3.scale(1/S, vecB);
+        
+        // Estimated state and covariance.
+        this._vecX_est = Vector3.add(vecX_prd, Vector3.scale(current - Vector3.multiplyVector3(this._vecH, vecX_prd), vecGain));
+        this._matP_est = Matrix3.subtract(matP_prd, Matrix3.scale(Vector3.multiplyVector3(vecGain, this._vecH), matP_prd));
+
+        // Compute the estimated measurement.
+        return Vector3.multiplyVector3(this._vecH,  this._vecX_est);
+    }
+}
diff --git a/PerformanceTests/Animometer/tests/resources/stage.css b/PerformanceTests/Animometer/tests/resources/stage.css
new file mode 100644 (file)
index 0000000..90e31bb
--- /dev/null
@@ -0,0 +1,17 @@
+html {
+    height: 100%;
+}
+body {
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+    margin: 0;
+    padding: 0;
+    background-color: #eaeaea;
+}
+
+#stage {
+  width: 100%;
+  height: 100%;
+  background-color: #eaeaea;
+}
diff --git a/PerformanceTests/Animometer/tests/resources/stage.js b/PerformanceTests/Animometer/tests/resources/stage.js
new file mode 100644 (file)
index 0000000..6d6ed24
--- /dev/null
@@ -0,0 +1,179 @@
+function Rotater(rotateInterval)
+{
+    this._timeDelta = 0;
+    this._rotateInterval = rotateInterval;
+}
+
+Rotater.prototype =
+{
+    get interval()
+    {
+        return this._rotateInterval;
+    },
+    
+    next: function(timeDelta)
+    {
+        this._timeDelta = (this._timeDelta + timeDelta) % this._rotateInterval;
+    },
+    
+    degree: function()
+    {
+        return (360 * this._timeDelta) / this._rotateInterval;
+    },
+    
+    rotateZ: function()
+    {
+        return "rotateZ(" + Math.floor(this.degree()) + "deg)";
+    },
+    
+    rotate: function(center)
+    {
+        return "rotate(" + Math.floor(this.degree()) + ", " + center.x + "," + center.y + ")";
+    }
+}
+
+function Stage(element, options)
+{
+    this.element = element;
+}
+
+Stage.prototype =
+{
+    get size()
+    {
+        var styles = window.getComputedStyle(this.element);
+        var padding = new Insets(
+            parseFloat(styles.paddingTop),
+            parseFloat(styles.paddingRight),
+            parseFloat(styles.paddingBottom),
+            parseFloat(styles.paddingTop));
+        return new Point(this.element.clientWidth - padding.width, this.element.clientHeight - padding.height);
+    },
+    
+    random: function(min, max)
+    {
+        return (Math.random() * (max - min)) + min;
+    },
+    
+    randomInt: function(min, max)
+    {
+        return Math.floor(this.random(min, max));
+    },
+    
+    randomPosition: function(maxPosition)
+    {
+        return new Point(this.randomInt(0, maxPosition.x), this.randomInt(0, maxPosition.y));
+    },
+    
+    randomSquareSize: function(min, max)
+    {
+        var side = this.random(min, max);
+        return new Point(side, side);
+    },
+    
+    randomVelocity: function(maxVelocity)
+    {
+        return this.random(maxVelocity / 8, maxVelocity);
+    },
+    
+    randomAngle: function()
+    {
+        return this.random(0, Math.PI * 2);
+    },
+    
+    randomColor: function()
+    {
+        var min = 32;
+        var max = 256 - 32;
+        return "#"
+            + this.randomInt(min, max).toString(16)
+            + this.randomInt(min, max).toString(16)
+            + this.randomInt(min, max).toString(16);
+    },
+    
+    randomRotater: function()
+    {
+        return new Rotater(this.random(1000, 10000));
+    },
+
+    tune: function()
+    {
+        throw "Not implemented";
+    },
+    
+    animate: function()
+    {
+        throw "Not implemented";
+    },
+    
+    clear: function()
+    {
+        return this.tune(-this.tune(0));
+    }
+}
+
+function StageAnimator(benchmark)
+{
+    Animator.call(this, benchmark);
+};
+
+StageAnimator.prototype = Object.create(Animator.prototype);
+StageAnimator.prototype.constructor = StageAnimator;
+
+StageAnimator.prototype.animate = function()
+{
+    if (!Animator.prototype.animate.call(this))
+        return false;
+    this._benchmark._stage.animate(this.timeDelta());
+    return true;
+}
+
+function StageBenchmark(suite, test, options, recordTable, progressBar)
+{
+    Benchmark.call(this, options);
+    
+    var element = document.getElementById("stage");
+    element.setAttribute("width", document.body.offsetWidth);
+    element.setAttribute("height", document.body.offsetHeight);
+    
+    this._stage = this.createStage(element);
+    this._animator = this.createAnimator();
+    this._suite = suite;
+    this._test = test;
+    this._recordTable = recordTable;
+    this._progressBar = progressBar;
+}
+
+StageBenchmark.prototype = Object.create(Benchmark.prototype);
+StageBenchmark.prototype.constructor = StageBenchmark;
+
+StageBenchmark.prototype.createStage = function(element)
+{
+    return new Stage(element, this._options);
+}
+
+StageBenchmark.prototype.createAnimator = function()
+{
+    return new StageAnimator(this);
+}
+
+StageBenchmark.prototype.tune = function(count)
+{
+    return this._stage.tune(count);
+}
+
+StageBenchmark.prototype.clear = function()
+{
+    return this._stage.clear();
+}
+
+StageBenchmark.prototype.showResults = function(message, progress)
+{
+    if (!this._recordTable || !this._progressBar || !this._suite || !this._test)
+        return;
+
+    if (this.options.showRunningResults)
+        this._recordTable.showRecord(this._suite, this._test, this._sampler, message);
+
+    this._progressBar.setPos(progress);
+}
diff --git a/PerformanceTests/Animometer/tests/resources/utilities.js b/PerformanceTests/Animometer/tests/resources/utilities.js
new file mode 100644 (file)
index 0000000..0726001
--- /dev/null
@@ -0,0 +1,60 @@
+window.Utilities =
+{
+    _parse: function(str, sep)
+    {
+        var output = {};
+        str.split(sep).forEach(function(part) {
+            var item = part.split("=");
+            var value = decodeURIComponent(item[1]);
+            if (value[0] == "'" )
+                output[item[0]] = value.substr(1, value.length - 2);
+            else
+                output[item[0]] = value;                
+          });
+        return output;
+    },
+    
+    parseParameters: function()
+    {
+        return this._parse(window.location.search.substr(1), "&");
+    },
+    
+    parseArguments: function(str)
+    {
+        return this._parse(str, " ");
+    },
+    
+    extendObject: function(obj1, obj2)
+    {
+        for (var attrname in obj2)
+            obj1[attrname] = obj2[attrname];
+        return obj1;
+    },
+    
+    copyObject: function(obj)
+    {
+        return this.extendObject({}, obj);
+    },
+    
+    mergeObjects: function(obj1, obj2)
+    {
+        return this.extendObject(this.copyObject(obj1), obj2);
+    },
+    
+    createSvgElement: function(name, attrs, xlinkAttrs, parent)
+    {
+        const svgNamespace = "http://www.w3.org/2000/svg";
+        const xlinkNamespace = "http://www.w3.org/1999/xlink";
+
+        var element = document.createElementNS(svgNamespace, name);
+        
+        for (var key in attrs)
+            element.setAttribute(key, attrs[key]);
+            
+        for (var key in xlinkAttrs)
+            element.setAttributeNS(xlinkNamespace, key, xlinkAttrs[key]);
+            
+        parent.appendChild(element);
+        return element;
+    }
+}
diff --git a/PerformanceTests/Animometer/tests/resources/yin-yang.png b/PerformanceTests/Animometer/tests/resources/yin-yang.png
new file mode 100644 (file)
index 0000000..3162f6e
Binary files /dev/null and b/PerformanceTests/Animometer/tests/resources/yin-yang.png differ
diff --git a/PerformanceTests/Animometer/tests/resources/yin-yang.svg b/PerformanceTests/Animometer/tests/resources/yin-yang.svg
new file mode 100644 (file)
index 0000000..4412626
--- /dev/null
@@ -0,0 +1,17 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200">\r
+    <defs>\r
+        <clipPath id="left-half">\r
+            <rect width="100" height="200"/>\r
+        </clipPath>\r
+        <clipPath id="right-half">\r
+            <rect x="100" width="100" height="200"/>\r
+        </clipPath>\r
+    </defs>\r
+    <circle cx="100" cy="100" r="98" fill="none" stroke="green" stroke-width="2"/>\r
+    <circle cx="100" cy="100" r="98" fill="white" clip-path="url(#left-half)"/>\r
+    <circle cx="100" cy="100" r="98" fill="green" clip-path="url(#right-half)"/>\r
+    <circle cx="100" cy="50"  r="49" fill="green"/>\r
+    <circle cx="100" cy="148" r="49" fill="white"/>\r
+    <circle cx="100" cy="50"  r="10" fill="white"/>    \r
+    <circle cx="100" cy="148" r="10" fill="green"/>\r
+</svg>\r
diff --git a/PerformanceTests/Animometer/tests/template/resources/template-canvas.js b/PerformanceTests/Animometer/tests/template/resources/template-canvas.js
new file mode 100644 (file)
index 0000000..7ee006d
--- /dev/null
@@ -0,0 +1,104 @@
+function TemplateCanvasObject(stage)
+{
+    // For the canvas stage, most likely you will need to create your
+    // animated object since it's only draw time thing.
+    
+    // Fill in your object data.
+}
+
+TemplateCanvasObject.prototype._draw = function()
+{
+    // Draw your object.
+}
+
+TemplateCanvasObject.prototype.animate = function(timeDelta)
+{
+    // Redraw the animated object. The last time this animated
+    // item was drawn before 'timeDelta'.
+    
+    // Move your object.
+    
+    // Redraw your object.
+    this._draw();
+}
+
+function TemplateCanvasStage(element, options)
+{
+    Stage.call(this, element, options);
+    this.context = this.element.getContext("2d");
+
+    // Define a collection for your objects.
+}
+
+TemplateCanvasStage.prototype = Object.create(Stage.prototype);
+TemplateCanvasStage.prototype.constructor = TemplateCanvasStage;
+
+TemplateCanvasStage.prototype.tune = function(count)
+{
+    // If count is -ve, -count elements need to be removed form the
+    // stage. If count is +ve, +count elements need to be added to
+    // the stage.
+    
+    // Change objects in the stage.
+    
+    // Return the number of all the elements in the stage.
+    // This number is recorded in the sampled data.
+    
+    // Return the count of the objects in the stage.
+    return 0;
+}
+
+TemplateCanvasStage.prototype.animate = function(timeDelta)
+{
+    // Animate the elements such that all of them are redrawn. Most
+    // likely you will need to call TemplateCanvasObject.animate()
+    // for all your animated objects here.
+
+    // Loop through all your objects and ask them to animate.
+}
+
+function TemplateCanvasAnimator(benchmark)
+{
+    Animator.call(this, benchmark);
+    this._context = benchmark._stage.context;
+}
+
+TemplateCanvasAnimator.prototype = Object.create(StageAnimator.prototype);
+TemplateCanvasAnimator.prototype.constructor = TemplateCanvasAnimator;
+
+TemplateCanvasAnimator.prototype.animate = function()
+{
+    // Most likely you will need to clear the canvas with every redraw.
+    this._context.clearRect(0, 0, this._benchmark._stage.size.x, this._benchmark._stage.size.y);
+
+    // Draw scene stuff here if needed.
+
+    return StageAnimator.prototype.animate.call(this);
+}
+
+function TemplateCanvasBenchmark(suite, test, options, recordTable, progressBar)
+{
+    StageBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+TemplateCanvasBenchmark.prototype = Object.create(StageBenchmark.prototype);
+TemplateCanvasBenchmark.prototype.constructor = TemplateCanvasBenchmark;
+
+TemplateCanvasBenchmark.prototype.createStage = function(element)
+{
+    // Attach the stage to the benchmark.
+    return new TemplateCanvasStage(element, this._options);
+}
+
+TemplateCanvasBenchmark.prototype.createAnimator = function()
+{
+    // Attach the animator to the benchmark.
+    return new TemplateCanvasAnimator(this);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    // This function is called from the test harness which starts the
+    // test by creating your benchmark object.
+    return new TemplateCanvasBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/template/resources/template-css.js b/PerformanceTests/Animometer/tests/template/resources/template-css.js
new file mode 100644 (file)
index 0000000..11ce961
--- /dev/null
@@ -0,0 +1,55 @@
+function TemplateCssStage(element, options)
+{
+    Stage.call(this, element, options);
+}
+
+TemplateCssStage.prototype = Object.create(Stage.prototype);
+TemplateCssStage.prototype.constructor = TemplateCssStage;
+
+TemplateCssStage.prototype.tune = function(count)
+{
+    // If count is -ve, -count elements need to be removed form the
+    // stage. If count is +ve, +count elements need to be added to
+    // the stage.
+    
+    // Change objects in the stage.
+    
+    // Return the number of all the elements in the stage.
+    // This number is recorded in the sampled data.
+    
+    // Return the count of the objects in the stage.
+    return 0;
+}
+
+TemplateCssStage.prototype.animate = function(timeDelta)
+{
+    // Animate the elements such that all of them are redrawn. You 
+    // may need to define your object so it keeps its animation data.
+    // This object should encapsulate a corrosponding HTMLElement.
+    // You may also define a method called animate() in this object
+    // and just call this function here for all the elements.
+    
+    // Loop through all your objects and ask them to animate.
+}
+
+function TemplateCssBenchmark(suite, test, options, recordTable, progressBar)
+{
+    StageBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+TemplateCssBenchmark.prototype = Object.create(StageBenchmark.prototype);
+TemplateCssBenchmark.prototype.constructor = TemplateCssBenchmark;
+
+TemplateCssBenchmark.prototype.createStage = function(element)
+{
+    // You need to override this method such that your stage is hooked
+    // up to the benchmark.
+    return new TemplateCssStage(element, this._options);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    // This function is called from the test harness which starts the
+    // test by creating your benchmark object.
+    return new TemplateCssBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/template/resources/template-svg.js b/PerformanceTests/Animometer/tests/template/resources/template-svg.js
new file mode 100644 (file)
index 0000000..48a84fa
--- /dev/null
@@ -0,0 +1,55 @@
+function TemplateSvgStage(element, options)
+{
+    Stage.call(this, element, options);
+}
+
+TemplateSvgStage.prototype = Object.create(Stage.prototype);
+TemplateSvgStage.prototype.constructor = TemplateSvgStage;
+
+TemplateSvgStage.prototype.tune = function(count)
+{
+    // If count is -ve, -count elements need to be removed form the
+    // stage. If count is +ve, +count elements need to be added to
+    // the stage.
+    
+    // TODO: Change objects in the stage.
+    
+    // Return the number of all the elements in the stage.
+    // This number is recorded in the sampled data.
+    
+    // TODO: Return the count of the objects in the stage.
+    return 0;
+}
+
+TemplateSvgStage.prototype.animate = function(timeDelta)
+{
+    // Animate the elements such that all of them are redrawn. You 
+    // may need to define your object so it keeps its animation data.
+    // This object should encapsulate a corrosponding SVGElement.
+    // You may also define a method called animate() in this object
+    // and just call this function here for all the elements.
+    
+    // TODO: Loop through all your objects and ask them to animate.
+}
+
+function TemplateSvgBenchmark(suite, test, options, recordTable, progressBar)
+{
+    StageBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+TemplateSvgBenchmark.prototype = Object.create(StageBenchmark.prototype);
+TemplateSvgBenchmark.prototype.constructor = TemplateSvgBenchmark;
+
+TemplateSvgBenchmark.prototype.createStage = function(element)
+{
+    // You need to override this method such that your stage is hooked
+    // up to the benchmark.
+    return new TemplateSvgStage(element, this._options);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    // This function is called from the test harness which starts the
+    // test by creating your benchmark object.
+    return new TemplateSvgBenchmark(suite, test, options, recordTable, progressBar);
+}
diff --git a/PerformanceTests/Animometer/tests/template/template-canvas.html b/PerformanceTests/Animometer/tests/template/template-canvas.html
new file mode 100644 (file)
index 0000000..bbf9695
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>
+    <script src="resources/template-canvas.js"></script>
+</head>
+<body>
+    <canvas id="stage"></canvas>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/template/template-css.html b/PerformanceTests/Animometer/tests/template/template-css.html
new file mode 100644 (file)
index 0000000..e7d246d
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>
+    <script src="resources/template-css.js"></script>
+</head>
+<body>
+    <div id="stage"></div>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/template/template-svg.html b/PerformanceTests/Animometer/tests/template/template-svg.html
new file mode 100644 (file)
index 0000000..cfaf70e
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>
+    <script src="resources/template-svg.js"></script>
+</head>
+<body>
+    <svg id="stage"></svg>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/text/layering-text.html b/PerformanceTests/Animometer/tests/text/layering-text.html
new file mode 100644 (file)
index 0000000..d7e3d05
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        .text-layer {
+            font-size: 5.5px;
+            position: absolute;
+            left: 0px;
+            top: 0px;
+            padding: 3px;
+            background-color: transparent;
+        }
+    </style>  
+    <link rel="stylesheet" type="text/css" href="../resources/stage.css">
+    <script src="../../resources/algorithm.js"></script>
+    <script src="../../resources/sampler.js"></script>
+    <script src="../../resources/extensions.js"></script>
+    <script src="../resources/math.js"></script>
+    <script src="../resources/utilities.js"></script>    
+    <script src="../resources/main.js"></script>
+    <script src="../resources/stage.js"></script>  
+    <script src="resources/layering-text.js"></script>
+</head>
+<body>
+    <div id="stage"></div>
+</body>
+</html>
diff --git a/PerformanceTests/Animometer/tests/text/resources/layering-text.js b/PerformanceTests/Animometer/tests/text/resources/layering-text.js
new file mode 100644 (file)
index 0000000..b616c1e
--- /dev/null
@@ -0,0 +1,257 @@
+function LayeringTextStage(element, options)
+{
+    Stage.call(this, element, options);
+    this._textElementParent = this.element;
+    this._textElements = [];
+    this._textItemIndex = 0;
+    this._colorIndex = 0;
+    this._animateCounts = 0;
+}
+
+LayeringTextStage.textItems = [
+    "<div class='text-layer'>",
+        "<h2>Types of benchmarks</h2>",
+        "<ol>",
+            "<li>Real program",
+                "<ul>",
+                    "<li>word processing software</li>",
+                    "<li>tool software of CAD</li>",
+                    "<li>user's application software (i.e.: MIS)</li>",
+                "</ul>",
+            "</li>",
+            "<li>Kernel",
+                "<ul>",
+                    "<li>contains key codes</li>",
+                    "<li>normally abstracted from actual program</li>",
+                    "<li>popular kernel: Livermore loop</li>",
+                    "<li>linpack benchmark (contains basic linear algebra subroutine written in FORTRAN language)</li>",
+                    "<li>results are represented in MFLOPS</li>",
+                "</ul>",
+            "</li>",
+            "<li>Synthetic Benchmark",
+                "<ul>",
+                    "<li>Procedure for programming synthetic benchmark:",
+                        "<ul>",
+                            "<li>take statistics of all types of operations from many application programs</li>",
+                            "<li>get proportion of each operation</li>",
+                            "<li>write program based on the proportion above</li>",
+                        "</ul>",
+                    "</li>",
+                    "<li>Types of Synthetic Benchmark are:",
+                        "<ul>",
+                            "<li>Whetstone</li>",
+                            "<li>Dhrystone</li>",
+                        "</ul>",
+                    "</li>",
+                    "<li>These were the first general purpose industry standard computer benchmarks. They do not necessarily obtain high scores on modern pipelined computers.</li>",
+                "</ul>",
+            "</li>",
+            "<li>I/O benchmarks</li>",
+            "<li>Database benchmarks: to measure the throughput and response times of database management systems (DBMS')</li>",
+            "<li>Parallel benchmarks: used on machines with multiple cores, processors or systems consisting of multiple machines</li>",
+        "</ol>",
+        "<h2>Common benchmarks</h2>",
+        "<ul>",
+            "<li>Business Applications Performance Corporation (BAPCo)</li>",
+            "<li>Embedded Microprocessor Benchmark Consortium (EEMBC)</li>",
+            "<li>Standard Performance Evaluation Corporation (SPEC), in particular their SPECint and SPECfp</li>",
+            "<li>Transaction Processing Performance Council (TPC)</li>",
+            "<li>Coremark: Embedded computing benchmark</li>",
+        "</ul>",
+        "<h3>Open source benchmarks</h3>",
+        "<ul>",
+            "<li>AIM Multiuser Benchmark: composed of a list of tests that could be mixed to create a ‘load mix’ that would simulate a specific computer function on any UNIX-type OS.</li>",
+            "<li>Bonnie++: filesystem and hard drive benchmark</li>",
+            "<li>BRL-CAD: cross-platform architecture-agnostic benchmark suite based on multithreaded ray tracing performance; baselined against a VAX-11/780; and used since 1984 for evaluating relative CPU performance, compiler differences, optimization levels, coherency, architecture differences, and operating system differences.</li>",
+        "</ul>",
+    "</div>"
+];
+
+LayeringTextStage.parseTextItem = function(textItem)
+{
+    var parseResult = {};
+    parseResult.tagStart = textItem.match(/<(.*?)>/g)[0];
+    var spaceIndex = parseResult.tagStart.indexOf(" ");
+    parseResult.nodeName = parseResult.tagStart.substring(1, spaceIndex != -1 ? spaceIndex : parseResult.tagStart.length - 1);
+    parseResult.args = spaceIndex != -1 ? Utilities.parseArguments(parseResult.tagStart.substring(spaceIndex + 1, parseResult.tagStart.length - 1)) : {};
+    var tagEnd = "</" + parseResult.nodeName + ">";                
+    parseResult.tagEnd = textItem.endsWith(tagEnd) ? tagEnd : "";
+    return parseResult;
+}
+
+LayeringTextStage.isOpeningTextItem = function(textItem)
+{
+    return !LayeringTextStage.parseTextItem(textItem).tagEnd.length;
+}
+
+LayeringTextStage.isClosingTextItem = function(textItem)
+{
+    return textItem.indexOf("/") == +1;
+}
+
+LayeringTextStage.textItemsFlags = LayeringTextStage.textItems.map(function(textItem)
+{
+   var textItemFlags = {};
+   textItemFlags.isOpening = LayeringTextStage.isOpeningTextItem(textItem);
+   textItemFlags.isClosing = LayeringTextStage.isClosingTextItem(textItem);   
+   return textItemFlags;
+});
+
+LayeringTextStage.isColorableTextItem = function(textItemFlags)
+{
+    return !(textItemFlags.isOpening || textItemFlags.isClosing);
+}
+
+LayeringTextStage.isInsertableTextItem = function(textItemFlags)
+{
+    return !textItemFlags.isClosing;
+}
+
+LayeringTextStage.colorableTextItems = LayeringTextStage.textItemsFlags.filter(function(textItemFlags)
+{
+    return LayeringTextStage.isColorableTextItem(textItemFlags);
+}).length;
+
+LayeringTextStage.insertableTextItems = LayeringTextStage.textItemsFlags.filter(function(textItemFlags)
+{
+    return LayeringTextStage.isInsertableTextItem(textItemFlags);
+}).length;
+
+LayeringTextStage.colorIndexToTextElementIndex = function(colorIndex)
+{
+    var textElementIndex = 0;
+    var index = 0;
+    
+    for (var textItemIndex = 0; textItemIndex < LayeringTextStage.textItemsFlags.length; ++textItemIndex) {
+        if (LayeringTextStage.isColorableTextItem(LayeringTextStage.textItemsFlags[textItemIndex])) {
+            if (++index > colorIndex)
+                break;
+        }
+        if (LayeringTextStage.isInsertableTextItem(LayeringTextStage.textItemsFlags[textItemIndex]))
+            ++textElementIndex;
+    }
+    
+    return textElementIndex;
+}
+
+LayeringTextStage.prototype = Object.create(Stage.prototype);
+LayeringTextStage.prototype.constructor = LayeringTextStage;
+
+LayeringTextStage.prototype._nextTextItem = function(textItemFlags)
+{
+    var textItem = LayeringTextStage.textItems[this._textItemIndex];
+    Utilities.extendObject(textItemFlags, LayeringTextStage.textItemsFlags[this._textItemIndex]);
+    this._textItemIndex = (this._textItemIndex + 1) % LayeringTextStage.textItems.length;
+    return textItem;
+}
+
+LayeringTextStage.prototype._previousTextItem = function(textItemFlags)
+{
+    this._textItemIndex = (this._textItemIndex + LayeringTextStage.textItems.length - 1) % LayeringTextStage.textItems.length;
+    Utilities.extendObject(textItemFlags, LayeringTextStage.textItemsFlags[this._textItemIndex]);    
+    return LayeringTextStage.textItems[this._textItemIndex];
+}
+
+LayeringTextStage.prototype._pushTextElement = function()
+{
+    var textItemFlags = {};
+    var textItem = this._nextTextItem(textItemFlags);
+    for ( ; textItemFlags.isClosing; textItem = this._nextTextItem(textItemFlags))
+        this._textElementParent = this._textElementParent.parentNode;
+
+    var parseResult = LayeringTextStage.parseTextItem(textItem);
+    textItem = textItem.substring(parseResult.tagStart.length, textItem.length - parseResult.tagEnd.length);
+
+    var textElement = document.createElement(parseResult.nodeName);
+
+    for (var attrname in parseResult.args)
+        textElement.setAttribute(attrname, parseResult.args[attrname]);
+
+    this._textElementParent.appendChild(textElement);
+    
+    if (!parseResult.tagEnd.length)
+        this._textElementParent = textElement;
+
+    textElement.innerHTML = textItem;
+    
+    this._textElements.push(textElement);
+    return this._textElements.length;
+}
+
+LayeringTextStage.prototype._popTextElement = function()
+{
+    var textItemFlags = {};    
+    var textItem = this._previousTextItem(textItemFlags);
+    for ( ; textItemFlags.isClosing; textItem = this._previousTextItem(textItemFlags))
+        this._textElementParent = this._textElementParent.lastChild;
+
+    if (textItemFlags.isOpening)
+        this._textElementParent = this._textElementParent.parentNode;
+
+    this._textElements[this._textElements.length - 1].remove();
+
+    this._textElements.pop();
+    return this._textElements.length;
+}
+
+LayeringTextStage.prototype._colorTextItem = function(color)
+{
+    var textElementIndex = LayeringTextStage.colorIndexToTextElementIndex(this._colorIndex);
+    for ( ; textElementIndex < this._textElements.length; textElementIndex += LayeringTextStage.insertableTextItems)
+        this._textElements[textElementIndex].style.backgroundColor = color;
+}
+
+LayeringTextStage.prototype.animate = function(timeDelta)
+{
+    this._colorTextItem("transparent");
+    this._colorIndex = (this._colorIndex + 1) % LayeringTextStage.colorableTextItems;
+    this._colorTextItem("YellowGreen");
+    
+    var blackTextElements = Math.min(this._textElements.length, LayeringTextStage.insertableTextItems);
+    var i = 0;
+    for ( ; i < this._textElements.length - blackTextElements; ++i)
+        this._textElements[i].style.color = (this._animateCounts & 1) ? "LightYellow" : "white";
+
+    for ( ; i < this._textElements.length; ++i)
+        this._textElements[i].style.color = "black";
+        
+    ++this._animateCounts;
+}
+
+LayeringTextStage.prototype.tune = function(count)
+{
+    count = count > 0 ? Math.floor(count) : Math.ceil(count);
+
+    if (count == 0)
+        return this._textElements.length;
+
+    if (count > 0) {
+        for (var i = 0; i < count; ++i)
+            this._pushTextElement();
+        return this._textElements.length;
+    }
+        
+    count = Math.min(-count, this._textElements.length);
+    for (var i = 0; i < count; ++i)
+        this._popTextElement();
+
+    return this._textElements.length;
+}
+
+function LayeringTextBenchmark(suite, test, options, recordTable, progressBar)
+{
+    StageBenchmark.call(this, suite, test, options, recordTable, progressBar);
+}
+
+LayeringTextBenchmark.prototype = Object.create(StageBenchmark.prototype);
+LayeringTextBenchmark.prototype.constructor = LayeringTextBenchmark;
+
+LayeringTextBenchmark.prototype.createStage = function(element)
+{
+    return new LayeringTextStage(element, this._options);
+}
+
+window.benchmarkClient.create = function(suite, test, options, recordTable, progressBar)
+{
+    return new LayeringTextBenchmark(suite, test, options, recordTable, progressBar);
+}
index fec2627..8489ee3 100644 (file)
@@ -1,3 +1,374 @@
+2015-10-05  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        Add a graphics benchmark
+        https://bugs.webkit.org/show_bug.cgi?id=149053
+        <rdar://problem/18984169>
+
+        Reviewed by Dean Jackson.
+
+        Instead of measuring the FPS of the animation, this benchmark measures the
+        test complexity when rendering at a set-point FPS which should be lower
+        than 60 FPS. This benchmark tries to stay at the set-point FPS by using
+        a closed loop control system PID function. The gain of the system is passed
+        as a parameter when running the test. Measuring the FPS faithfully results
+        very fluctuating values. A Kalman filter is used to give a better estimate
+        for the current FPS.
+
+        The animation of the tests is done manually. requestAnimationFrame() is
+        called with a callback. Inside this callback, the test is animating by
+        changing the positions of the elements inside the page. The test complexity
+        may change also if the current FPS is not equal to the desired FPS.
+
+        In this patch, the benchmark and the tests are included. The shared code
+        and the tests runner are included in separate patches.
+
+        * Animometer/runner/animometer.html:
+        * Animometer/runner/resources/animometer.js:
+        Add two new examples for more complex animation techniques.
+        Add an option to show/hide the test running results which is off by default.
+
+        * Animometer/runner/resources/tests.js: Added.
+        (suiteFromName): Returns a suite given its name.
+        (testFromName): Returns a test given its suite and name.
+
+        * Animometer/tests: Added.
+        This directory includes all the test suites to be run by the benchmark.
+        runner. All the tests should try to run on three stages: CSS, canvas and
+        SVG.
+        
+        * Animometer/tests/bouncing-particles: Added.
+        * Animometer/tests/bouncing-particles/resources: Added.
+        The bouncing particles test is an example of a simple animation technique.
+        
+        * Animometer/tests/bouncing-particles/bouncing-canvas-images.html: Added.
+        * Animometer/tests/bouncing-particles/bouncing-canvas-shapes.html: Added.
+        * Animometer/tests/bouncing-particles/bouncing-css-images.html: Added.
+        * Animometer/tests/bouncing-particles/bouncing-css-shapes.html: Added.
+        * Animometer/tests/bouncing-particles/bouncing-svg-images.html: Added.
+        * Animometer/tests/bouncing-particles/bouncing-svg-shapes.html: Added.
+        Bouncing particles test pages.
+
+        * Animometer/tests/bouncing-particles/resources/bouncing-particles.js: Added.
+        (BouncingParticle): Base class for a bouncing particle.
+        (BouncingParticle.prototype.center): Returns the center point or the particle.
+        (BouncingParticle.prototype.animate): Moves the particle based on its current position, angle and velocity.
+        
+        (BouncingParticlesAnimator): A sub class of Animator.
+        
+        (BouncingParticlesStage): Represents the container of all the bouncing particles.
+        (BouncingParticlesStage.prototype.parseShapeParamters): Gets the shape parameters for shape animating tests.
+        (BouncingParticlesStage.prototype.randomRotater): Creates a rotater for the particles.
+        (BouncingParticlesStage.prototype.animate): Animates all the particles.
+        (BouncingParticlesStage.prototype.tune): Changes the test by adding or removing particles.
+        
+        (BouncingParticlesBenchmark): Runs the benchmark for bouncing particles test.
+        (BouncingParticlesBenchmark.prototype.createAnimator): Creates an animator of type BouncingParticlesAnimator.
+
+        * Animometer/tests/bouncing-particles/resources/bouncing-css-shapes.js: Added.
+        (BouncingCssShape): A sub class of BouncingParticle for animating CSS shapes.
+        (BouncingCssShape.prototype._createSpan): Creates a <span> element and takes the shape and clipping classes into consideration.
+        (BouncingCssShape.prototype._move): Moves the particle to a new location. Apply transform since it does not require layout.
+        (BouncingCssShape.prototype.animate): Rotates and moves the shape to a new location.
+        
+        (BouncingCssShapesStage): A sub class of BouncingParticlesStage for animating CSS shapes.
+        (BouncingCssShapesStage.prototype.createParticle): Creates a particle of type BouncingCssShape.
+        (BouncingCssShapesStage.prototype.particleWillBeRemoved): Removes the corresponding element form the parent children list.
+        
+        (BouncingCssShapesBenchmark): A sub class of BouncingParticlesBenchmark for animating CSS shapes.
+        (BouncingCssShapesBenchmark.prototype.createStage): Creates a stage of type BouncingCssShapesStage.
+        (window.benchmarkClient.create): Creates a benchmark of type BouncingCssShapesBenchmark.
+        
+        * Animometer/tests/bouncing-particles/resources/bouncing-css-images.js: Added.
+        (BouncingCssImage): A sub class of BouncingParticle for animating CSS images.
+        (BouncingCssImage.prototype._move): Move the particle to a new location. Apply transform since it does not require layout.
+        (BouncingCssImage.prototype.animate): Rotates and moves the shape to a new location.
+        
+        (BouncingCssImagesStage): A sub class of BouncingParticlesStage for animating CSS images.
+        (BouncingCssImagesStage.prototype.createParticle): Creates a particle of type BouncingCssImage.
+        (BouncingCssImagesStage.prototype.particleWillBeRemoved): Removes the corresponding element form the parent children list.
+        
+        (BouncingCssImagesBenchmark): A sub class of BouncingParticlesBenchmark for animating CSS images.
+        (BouncingCssImagesBenchmark.prototype.createStage): Creates a stage of type BouncingCssImagesStage.
+        (window.benchmarkClient.create): Creates a benchmark of type BouncingCssImagesBenchmark.
+        
+        * Animometer/tests/bouncing-particles/resources/bouncing-canvas-particles.js: Added.
+        (BouncingCanvasParticle): A base sub-class of BouncingParticle for animating canvas particles.
+        (BouncingCanvasParticle.prototype._applyRotation): Apply the particle rotation-around-center transform to the canvas context.
+        (BouncingCanvasParticle.prototype._applyClipping): Apply the particle clipping to the canvas context.
+        (BouncingCanvasParticle.prototype._draw): A non-implemented version of the drawing function.
+        (BouncingCanvasParticle.prototype.animate): Carries out all the steps to redraw the canvas particle.
+        
+        (BouncingCanvasParticlesStage): A base sub-class of BouncingParticlesStage for animating canvas particles.
+        
+        (BouncingCanvasParticlesAnimator): A concrete sub-class of BouncingParticlesAnimator for animating canvas particles.
+        (BouncingCanvasParticlesAnimator.prototype.animate): Overrides the base class method to clear the canvas before redrawing the stage.
+        
+        (BouncingCanvasParticlesBenchmark): A base sub-class of StageBenchmark for animating canvas particles.
+        (BouncingCanvasParticlesBenchmark.prototype.createAnimator): Creates the canvas particles animator.
+        
+        * Animometer/tests/bouncing-particles/resources/bouncing-canvas-shapes.js: Added.
+        (BouncingCanvasShape): A concrete sub-class of BouncingCanvasParticle for animating canvas shapes.
+        (BouncingCanvasShape.prototype._applyFill): Sets the fillStyle in the canvas context.
+        (BouncingCanvasShape.prototype._drawShape): Carries out the actual drawing.
+        (BouncingCanvasShape.prototype._draw): Carries out all the steps to draw the shape.
+        
+        (BouncingCanvasShapesStage): A concrete sub-class of BouncingCanvasParticle for animating canvas shapes.
+        (BouncingCanvasShapesStage.prototype.createParticle): Creates a particle of type BouncingCanvasShape.
+        
+        (BouncingCanvasShapesBenchmark): A concrete sub-class of BouncingCanvasParticlesBenchmark for animating canvas shapes.
+        (BouncingCanvasShapesBenchmark.prototype.createStage): Creates a stage of type BouncingCanvasShapesStage.
+        (window.benchmarkClient.create): Creates a benchmark of type BouncingCanvasShapesBenchmark.
+
+        * Animometer/tests/bouncing-particles/resources/bouncing-canvas-images.js: Added.
+        (BouncingCanvasImage): A concrete sub-class of BouncingCanvasParticle for animating canvas images.
+        (BouncingCanvasImage.prototype._draw): Draws an image on the context of a canvas.
+        
+        (BouncingCanvasImagesStage): A concrete sub-class of BouncingCanvasParticlesBenchmark for animating canvas images.
+        (BouncingCanvasImagesStage.prototype.createParticle): Creates a particle of type BouncingCanvasImage.
+        
+        (BouncingCanvasImagesBenchmark): A concrete sub-class of BouncingCanvasParticlesBenchmark for animating canvas images.
+        (BouncingCanvasImagesBenchmark.prototype.createStage): Creates a stage of type BouncingCanvasImagesStage.
+        (window.benchmarkClient.create): Creates a benchmark of type BouncingCanvasImagesBenchmark.
+
+        * Animometer/tests/bouncing-particles/resources/bouncing-svg-particles.js: Added.
+        (BouncingSvgParticle): A base sub-class of BouncingParticle for animating SVG particles.
+        (BouncingSvgParticle.prototype._applyClipping): Apply the particle clipping by setting the 'clip-path' attribute of the SVGElement.
+        (BouncingSvgParticle.prototype._move): Moves the particle to a new location. Apply transform since it does not require layout.
+        (BouncingSvgParticle.prototype.animate): Rotates and moves the shape to a new location.
+        (BouncingSvgParticlesStage): A sub class of BouncingParticlesStage for animating SVGElements.
+        (BouncingSvgParticlesStage.prototype._createDefs): Creates an SVGDefsElement.
+        (BouncingSvgParticlesStage.prototype._ensureDefsIsCreated): Ensures there is only one SVGDefsElement is created.
+        (BouncingSvgParticlesStage.prototype._createClipStar): Creates an SVGClipPathElement and sets its 'd' attribute to a star like shape.
+        (BouncingSvgParticlesStage.prototype.ensureClipStarIsCreated): Ensure there is only one star SVGClipPathElement is created.
+        (BouncingSvgParticlesStage.prototype.particleWillBeRemoved): Remove the corresponding element form the parent children list.
+
+        * Animometer/tests/bouncing-particles/resources/bouncing-svg-shapes.js: Added.
+        (BouncingSvgShape): A concrete sub-class of BouncingSVGParticle for animating SVG shapes.
+        (BouncingSvgShape.prototype._createShape): Creates an SVG shape.
+        (BouncingSvgShape.prototype._applyFill): Applies the selected fill style to the SVG shape.
+        
+        (BouncingSvgShapesStage): A concrete sub-class of BouncingSvgParticlesStage for animating SVG shapes.
+        (BouncingSvgShapesStage.prototype.createGradient): Creates an SVGLinearGradientElement.
+        (BouncingSvgShapesStage.prototype.createParticle): Creates a particle of type BouncingSvgShape.
+        (BouncingSvgShapesStage.prototype.particleWillBeRemoved): Ensures the attached SVGLinearGradientElement is removed from the SVGDefsElement.
+        
+        (BouncingSvgShapesBenchmark): A concrete sub-class of BouncingParticlesBenchmark for animating SVG images.
+        (BouncingSvgShapesBenchmark.prototype.createStage): Creates a stage of type BouncingSvgShapesStage.
+        (window.benchmarkClient.create): Creates a benchmark of type BouncingSvgShapesBenchmark.
+        
+        * Animometer/tests/bouncing-particles/resources/bouncing-svg-images.js: Added.
+        (BouncingSvgImage): A concrete sub-class of BouncingSVGParticle for animating SVG images.
+        
+        (BouncingSvgImagesStage): A concrete sub-class of BouncingSVGParticlesBenchmark for animating SVG images.
+        (BouncingSvgImagesStage.prototype.createParticle): Creates a particle of type BouncingSvgImage.
+        
+        (BouncingSvgImagesBenchmark): A concrete sub-class of BouncingParticlesBenchmark for animating SVG images.
+        (BouncingSvgImagesBenchmark.prototype.createStage): Creates a stage of type BouncingSvgImagesStage.
+        (window.benchmarkClient.create): Creates a benchmark of type BouncingSvgImagesBenchmark.
+
+        * Animometer/tests/examples: Added.
+        * Animometer/tests/examples/canvas-electrons.html: Added.
+        * Animometer/tests/examples/canvas-stars.html: Added.
+        Examples test pages. 
+        
+        * Animometer/tests/examples/resources: Added.
+        * Animometer/tests/examples/resources/canvas-electrons.js: Added.
+        (CanvasElectron): An object which draws and animate a electron object on a canvas stage.
+        (CanvasElectron.prototype._draw): Draws the electron object.
+        (CanvasElectron.prototype.animate): Animates the electron object.
+        
+        (CanvasElectronsStage): A concrete sub-class of Stage for animating electrons.
+        (CanvasElectronsStage.prototype.tune): Changes the test by adding or removing elements.
+        (CanvasElectronsStage.prototype.animate): Animates the test elements.
+        
+        (CanvasElectronsAnimator): A concrete sub-class of StageAnimator for animating canvas electrons.
+        (CanvasElectronsAnimator.prototype.animate): Overrides the base class method to clear the canvas before redrawing the stage.
+        
+        (CanvasElectronsBenchmark): A concrete sub-class of StageBenchmark for animating electrons.
+        (CanvasElectronsBenchmark.prototype.createStage): Creates a stage of CanvasElectronsStage.
+        (CanvasElectronsBenchmark.prototype.createAnimator): Creates an animator of type CanvasElectronsAnimator.
+        (window.benchmarkClient.create): Creates a benchmark of type CanvasElectronsBenchmark.
+        
+        * Animometer/tests/examples/resources/canvas-stars.js: Added.
+        (CanvasStar): An object which draws and animate a star object on a canvas stage.
+        (CanvasStar.prototype._draw): Draws the star object.
+        (CanvasStar.prototype.animate): Animates the star object.
+        
+        (CanvasStarsStage): A concrete sub-class of Stage for animating stars.
+        (CanvasStarsStage.prototype.tune): Changes the test by adding or removing elements.
+        (CanvasStarsStage.prototype.animate): Animates the test elements.
+        
+        (CanvasStarsAnimator): A concrete sub-class of StageAnimator for animating canvas stars.
+        (CanvasStarsAnimator.prototype.animate): Overrides the base class method to clear the canvas before redrawing the stage.
+        
+        (CanvasStarsBenchmark): A concrete sub-class of Benchmark for animating stars.
+        (CanvasStarsBenchmark.prototype.createStage): Creates a stage of CanvasStarsStage.
+        (CanvasStarsBenchmark.prototype.createAnimator): Creates an animator of type CanvasStarsAnimator.
+        (window.benchmarkClient.create): Creates a benchmark of type CanvasStarsBenchmark.
+
+        * Animometer/tests/resources: Added.
+        This directory includes the script which is required to run an adaptive 
+        graphics benchmark. From an empty test page, the set of classes in this
+        directory are responsible for measuring the current frame rate and
+        changing the test to reach a desired FPS. It keeps asking the test page
+        to tune itself by a certain value to increase or decrease the frame rate.
+        It's also responsible for sampling the test state and the corresponding
+        frame rate.
+        
+        * Animometer/tests/resources/main.js: Added.
+        (BenchmarkState): Tracks the state of the benchmark test.
+        (BenchmarkState.prototype._timeOffset): Returns the timeOffset of a stage.
+        (BenchmarkState.prototype._message): Returns the message of a stage.
+        (BenchmarkState.prototype.update): Sets the currentTimeOffset to a new value.
+        (BenchmarkState.prototype.samplingTimeOffset): Returns the timeOffset of the sampling stage.
+        (BenchmarkState.prototype.currentStage): Returns the current stage of the benchmark.
+        (BenchmarkState.prototype.currentMessage): Returns the message of the current stage and timeOffset.
+        (BenchmarkState.prototype.currentProgress): Returns a percentage of how much the benchmark is running.
+        
+        (Animator): Manages animating the test.
+        (Animator.prototype.start): Called if animating using setInterval is requested.
+        (Animator.prototype.timeDelta): Returns the current timeDelta 
+        (Animator.prototype.animate): Manages the test animation.
+        (Animator.prototype.animateLoop): Called if animating using requestAnimationFrame is requested. 
+
+        (Benchmark): Manages running the test benchmark and recording the sampled data.
+        (Benchmark.prototype.start): Starts the benchmark.
+        (Benchmark.prototype.update): Called from the animator.animate() to change the complexity of the test.
+        (Benchmark.prototype.record): Shows the current (not final) results of the benchmark.
+        (Benchmark.prototype.resolveWhenFinished): Spins until the benchmark is finished and returns its results.
+        (Benchmark.prototype.run): Starts the test, runs it, waits until it is finished and return its results.
+        (window.runBenchmark): Called from the benchmark runner through the suite controller run-callback.
+
+        * Animometer/tests/resources/math.js: Added.
+        (Matrix): A matrix object.
+        (Vector3): A vector of size 3 object.
+        (Matrix3): A matrix of size 3x3 object.
+        
+        (PIDController): Closed-loop controller for a set-point y.
+        (PIDController.prototype._sat): Limits the output to a certain range.
+        (PIDController.prototype.tune): Given the current output of a system, it produces a new pid value for tuning it.
+        
+        (KalmanEstimator): Implement Kalman filter to get an estimate for a sampled data point.
+        (KalmanEstimator.prototype.estimate): Returns an estimate for for a sampled data point.
+
+        * Animometer/tests/resources/utilities.js: Added.
+        (window.Utilities._parse): Given a separator character, it pareses a string to a set of <key, value> pairs.
+        (window.Utilities.parseParameters): Parses a test parameters.
+        (window.Utilities.parseArguments): Parses a tag arguments.
+        (window.Utilities.extendObject): Adds the attributes and their values of an object to another object.
+        (window.Utilities.copyObject): Copies the attributes and their values of an object to a new object.
+        (window.Utilities.mergeObjects): Copies the attributes and their values of two objects to a new object.
+        (window.Utilities.createSvgElement): Creates an SVGElement given its name and its attributes.
+        
+        * Animometer/tests/resources/stage.css: Added.
+        * Animometer/tests/resources/stage.js: Added.
+        (Rotater): Manages rotating an angle within a fixed time interval.
+        (Rotater.prototype.get interval): Returns the time interval which is required to rotate 360 degrees.
+        (Rotater.prototype.next): Moves the current time by a delta.
+        (Rotater.prototype.degree): Returns the current rotating degree.
+        (Rotater.prototype.rotateZ): Returns CSS formatted transform rotateZ() string for the current degree.
+        (Rotater.prototype.rotate): Returns SVG formatted transform rotate() string for the current degree.
+        
+        (Stage): A base class for managing the test complexity and test animation.
+        (Stage.prototype.get size): Returns the size of the stage excluding the CSS padding.
+        (Stage.prototype.random): Returns a random float.
+        (Stage.prototype.randomInt): Returns a random integer.
+        (Stage.prototype.randomPosition): Returns a random position.
+        (Stage.prototype.randomSquareSize): Returns a square size.
+        (Stage.prototype.randomVelocity): Returns a random velocity.
+        (Stage.prototype.randomAngle): Returns a random angle.
+        (Stage.prototype.randomColor): Returns a random color not too dark and not too light.
+        (Stage.prototype.randomRotater): Creates a random rotater. Its velocity depends on choosing a random rotation time interval.
+        (Stage.prototype.tune): A not-implemented version of this function.
+        (Stage.prototype.animate): A not-implemented version of this function.
+        (Stage.prototype.clear): Clears the stage from all its animation elements.
+        
+        (StageAnimator): A base class for the stage-based animators.
+        (StageAnimator.prototype.animate): Calls Animator.animate() which updates the test page and then calls Stage.animate() to force redraw.
+        
+        (StageBenchmark): A base class for the stage-based benchmarks.
+        (StageBenchmark.prototype.createStage): Creates the default stage.
+        (StageBenchmark.prototype.createAnimator): Creates the default animator.
+        (StageBenchmark.prototype.tune): Delegates the call to stage.
+        (StageBenchmark.prototype.clear): Delegates the call to stage.
+        (StageBenchmark.prototype.showResults): Shows the results/progress through its recordTable and progressBar.
+        
+        * Animometer/tests/resources/yin-yang.png: Added.
+        * Animometer/tests/resources/yin-yang.svg: Added.
+        These images are shared among all the tests.
+        
+        * Animometer/tests/template: Added.
+        * Animometer/tests/template/resources: Added.
+        This directory includes template tests which do nothing. They can be used
+        to author new tests. Animated items can be created, moved and redrawn by
+        removing the TODO comments in the script files and writing actual code.
+
+        * Animometer/tests/template/template-css.html: Added.
+        * Animometer/tests/template/template-canvas.html: Added.
+        * Animometer/tests/template/template-svg.html: Added.
+        Template test pages. They can be used as they are. CSS attributes or hidden
+        elements can be added to these derived test pages if needed.
+        
+        * Animometer/tests/template/resources/template-css.js: Added.
+        
+        (TemplateCssStage): A stage to create and animate HTMLElements.
+        (TemplateCssStage.prototype.tune): Changes the test by adding or removing elements.
+        (TemplateCssStage.prototype.animate): Animates the test elements.
+        (TemplateCssBenchmark): 
+        (TemplateCssBenchmark.prototype.createStage): Creates the test stage.
+        (window.benchmarkClient.create): Creates a benchmark of type TemplateCssBenchmark.
+        
+        * Animometer/tests/template/resources/template-canvas.js: Added.
+        (TemplateCanvasObject):
+        (TemplateCanvasObject.prototype._draw): Draws the objects on the canvas context.
+        (TemplateCanvasObject.prototype.animate): Moves and redraws the object.
+        (TemplateCanvasStage): A stage to create and animate drawing elements.
+        (TemplateCanvasStage.prototype.tune): hanges the test by adding or removing elements.
+        (TemplateCanvasStage.prototype.animate):  Animates the test elements.
+        (TemplateCanvasAnimator.prototype.animate): Starts the animation every frame.
+        (TemplateCanvasBenchmark): 
+        (TemplateCanvasBenchmark.prototype.createStage): Creates a stage of type TemplateCanvasStage.
+        (TemplateCanvasBenchmark.prototype.createAnimator): Creates a animator of type TemplateCanvasAnimator.
+        (window.benchmarkClient.create): Creates a benchmark of type TemplateCanvasBenchmark.
+        
+        * Animometer/tests/template/resources/template-svg.js: Added.
+        (TemplateSvgStage): A stage to create and animate SVGElements.
+        (TemplateSvgStage.prototype.tune): Changes the test by adding or removing elements.
+        (TemplateSvgStage.prototype.animate): Animates the test elements.
+        (TemplateSvgBenchmark.prototype.createStage): Creates a stage of type TemplateSvgStage.
+        (window.benchmarkClient.create): Creates a benchmark of type TemplateSvgBenchmark.
+        
+        * Animometer/tests/text: Added.
+        * Animometer/tests/text/resources: Added.
+        This directory includes the text animating tests which currently runs
+        on CSS stage only.
+        
+        * Animometer/tests/text/layering-text.html: Added.
+        Text test page.
+        
+        * Animometer/tests/text/resources/layering-text.js: Added.
+        (LayeringTextStage): Represents the container of all the stacked text layers.
+        (LayeringTextStage.parseTextItem): Parses a textItem which may be an opening tag, a closing tag or a self-closing tag.
+        (LayeringTextStage.isOpeningTextItem): Returns true if the textItem is an opening tag e.g. '<ol>'.
+        (LayeringTextStage.isClosingTextItem): Returns true if  the textItem is an closing tag e.g. '</ol>.
+        (LayeringTextStage.textItemsFlags.LayeringTextStage.textItems.map): Calculates and stores isOpening and isClosing flags for each textItem.
+        (LayeringTextStage.isColorableTextItem): Returns true if the textItem is self-closing tag e.g. '<p>...</p>'.
+        (LayeringTextStage.isInsertableTextItem): Returns true if the textItems causes a new element to be added to the text layers.
+        (LayeringTextStage.colorableTextItems.LayeringTextStage.textItemsFlags.filter): Number of colorable textItems.
+        (LayeringTextStage.insertableTextItems.LayeringTextStage.textItemsFlags.filter): Number of insertable textItems.
+        (LayeringTextStage.colorIndexToTextElementIndex): Maps from colorIndex [0..colorableTextItems-1] to textElementIndex [0..insertableTextItems-1].
+        (LayeringTextStage.prototype._nextTextItem): Moves the _textItemIndex one step forward in a loop [0..LayeringTextStage.textItems.length-1].
+        (LayeringTextStage.prototype._previousTextItem): Moves the _textItemIndex one step backward in a loop.
+        (LayeringTextStage.prototype._pushTextElement): Creates a new textItemElement and adds it to the topmost container <div>.
+        (LayeringTextStage.prototype._popTextElement): Removes the last textItemElement from the topmost container <div>.
+        (LayeringTextStage.prototype._colorTextItem): Changes the background color of a single colorable textElement. The index advances in a circle [0..colorableTextItems-1].
+        (LayeringTextStage.prototype.animate): Changes the background color and the text color of the textElements such that a redraw is enforced.
+        (LayeringTextStage.prototype.tune): Adds or removes textElements to the stage.
+
+        (LayeringTextBenchmark): Runs the benchmark for the layering text test.
+        (LayeringTextBenchmark.prototype.createStage): Creates a stage of type LayeringTextStage.
+        (window.benchmarkClient.create): Creates a benchmark of type LayeringTextBenchmark.
+
 2015-10-02  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         Add shared code for a new a graphics benchmark