blob: 8087727bcffdf42fa6d6f2ed7d4b181e2ec675db [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/canvas2d/CanvasRenderingContext2D.h"
#include "core/dom/Document.h"
#include "core/fetch/MemoryCache.h"
#include "core/frame/FrameView.h"
#include "core/frame/Settings.h"
#include "core/html/HTMLCanvasElement.h"
#include "core/html/ImageData.h"
#include "core/loader/EmptyClients.h"
#include "core/testing/DummyPageHolder.h"
#include "modules/accessibility/AXObject.h"
#include "modules/accessibility/AXObjectCacheImpl.h"
#include "modules/canvas2d/CanvasGradient.h"
#include "modules/canvas2d/CanvasPattern.h"
#include "modules/canvas2d/HitRegionOptions.h"
#include "modules/webgl/WebGLRenderingContext.h"
#include "platform/graphics/StaticBitmapImage.h"
#include "platform/graphics/UnacceleratedImageBufferSurface.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Mock;
namespace blink {
namespace UsageTrackingTests {
enum BitmapOpacity {
OpaqueBitmap,
TransparentBitmap
};
class FakeImageSource : public CanvasImageSource {
public:
FakeImageSource(IntSize, BitmapOpacity);
PassRefPtr<Image> getSourceImageForCanvas(SourceImageStatus*, AccelerationHint, SnapshotReason, const FloatSize&) const override;
bool wouldTaintOrigin(SecurityOrigin* destinationSecurityOrigin) const override { return false; }
FloatSize elementSize(const FloatSize&) const override { return FloatSize(m_size); }
bool isOpaque() const override { return m_isOpaque; }
int sourceWidth() override { return m_size.width(); }
int sourceHeight() override { return m_size.height(); }
~FakeImageSource() override { }
private:
IntSize m_size;
RefPtr<Image> m_image;
bool m_isOpaque;
};
FakeImageSource::FakeImageSource(IntSize size, BitmapOpacity opacity)
: m_size(size)
, m_isOpaque(opacity == OpaqueBitmap)
{
sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(m_size.width(), m_size.height()));
surface->getCanvas()->clear(opacity == OpaqueBitmap ? SK_ColorWHITE : SK_ColorTRANSPARENT);
RefPtr<SkImage> image = adoptRef(surface->makeImageSnapshot().release());
m_image = StaticBitmapImage::create(image);
}
PassRefPtr<Image> FakeImageSource::getSourceImageForCanvas(SourceImageStatus* status, AccelerationHint, SnapshotReason, const FloatSize&) const
{
if (status)
*status = NormalSourceImageStatus;
return m_image;
}
class CanvasRenderingContextUsageTrackingTest : public ::testing::Test {
protected:
CanvasRenderingContextUsageTrackingTest();
void SetUp() override;
DummyPageHolder& page() const { return *m_dummyPageHolder; }
Document& document() const { return *m_document; }
HTMLCanvasElement& canvasElement() const { return *m_canvasElement; }
CanvasRenderingContext2D* context2d() const;
void createContext(OpacityMode);
FakeImageSource m_opaqueBitmap;
FakeImageSource m_alphaBitmap;
Persistent<ImageData> m_fullImageData;
void TearDown();
private:
std::shared_ptr<DummyPageHolder> m_dummyPageHolder;
Persistent<Document> m_document;
Persistent<HTMLCanvasElement> m_canvasElement;
Persistent<MemoryCache> m_globalMemoryCache;
};
CanvasRenderingContextUsageTrackingTest::CanvasRenderingContextUsageTrackingTest()
: m_opaqueBitmap(IntSize(10, 10), OpaqueBitmap)
, m_alphaBitmap(IntSize(10, 10), TransparentBitmap)
{ }
void CanvasRenderingContextUsageTrackingTest::TearDown()
{
ThreadHeap::collectGarbage(BlinkGC::NoHeapPointersOnStack, BlinkGC::GCWithSweep, BlinkGC::ForcedGC);
replaceMemoryCacheForTesting(m_globalMemoryCache.release());
}
CanvasRenderingContext2D* CanvasRenderingContextUsageTrackingTest::context2d() const
{
// If the following check fails, perhaps you forgot to call createContext
// in your test?
EXPECT_NE(nullptr, canvasElement().renderingContext());
EXPECT_TRUE(canvasElement().renderingContext()->is2d());
return static_cast<CanvasRenderingContext2D*>(canvasElement().renderingContext());
}
void CanvasRenderingContextUsageTrackingTest::createContext(OpacityMode opacityMode)
{
String canvasType("2d");
CanvasContextCreationAttributes attributes;
attributes.setAlpha(opacityMode == NonOpaque);
m_canvasElement->getCanvasRenderingContext(canvasType, attributes);
context2d(); // Calling this for the checks
}
void CanvasRenderingContextUsageTrackingTest::SetUp()
{
Page::PageClients pageClients;
fillWithEmptyClients(pageClients);
m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600), &pageClients);
m_document = &m_dummyPageHolder->document();
m_document->documentElement()->setInnerHTML("<body><canvas id='c'></canvas></body>", ASSERT_NO_EXCEPTION);
m_document->view()->updateAllLifecyclePhases();
m_canvasElement = toHTMLCanvasElement(m_document->getElementById("c"));
m_fullImageData = ImageData::create(IntSize(10, 10));
m_globalMemoryCache = replaceMemoryCacheForTesting(MemoryCache::create());
}
TEST_F(CanvasRenderingContextUsageTrackingTest, FillTracking)
{
createContext(NonOpaque);
int numReps = 100;
for (int i = 0; i < numReps; i++) {
context2d()->beginPath();
context2d()->moveTo(1, 1);
context2d()->lineTo(4, 1);
context2d()->lineTo(4, 4);
context2d()->lineTo(2, 2);
context2d()->lineTo(1, 4);
context2d()->fill(); // path is not convex
context2d()->fillText("Hello World!!!", 10, 10, 1);
context2d()->fillRect(10, 10, 100, 100);
context2d()->fillText("Hello World!!!", 10, 10);
context2d()->beginPath();
context2d()->moveTo(1, 1);
context2d()->lineTo(4, 1);
context2d()->lineTo(4, 4);
context2d()->lineTo(1, 4);
context2d()->fill(); // path is convex
context2d()->fillRect(10, 10, 100, 100);
}
EXPECT_EQ(numReps * 2, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillPath]);
EXPECT_EQ(numReps * 2, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillRect]);
EXPECT_EQ(numReps, context2d()->getUsage().numNonConvexFillPathCalls);
EXPECT_EQ(numReps * 2, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillText]);
// areas
EXPECT_NEAR(static_cast<double>(numReps * (100 * 100) * 2), context2d()->getUsage().boundingBoxAreaDrawCalls[BaseRenderingContext2D::FillRect], 1.0);
EXPECT_NEAR(static_cast<double>(numReps * (2*100 + 2*100) * 2), context2d()->getUsage().boundingBoxPerimeterDrawCalls[BaseRenderingContext2D::FillRect], 1.0);
EXPECT_TRUE(static_cast<double>(numReps * (100 * 100) * 2) < context2d()->getUsage().boundingBoxAreaFillType[BaseRenderingContext2D::ColorFillType]);
EXPECT_TRUE(static_cast<double>(numReps * (100 * 100) * 2) * 1.1 > context2d()->getUsage().boundingBoxAreaFillType[BaseRenderingContext2D::ColorFillType]);
EXPECT_NEAR(static_cast<double>(numReps * (3 * 3) * 2), context2d()->getUsage().boundingBoxAreaDrawCalls[BaseRenderingContext2D::FillPath], 1.0);
EXPECT_NEAR(static_cast<double>(numReps * (2*3 + 2*3) * 2), context2d()->getUsage().boundingBoxPerimeterDrawCalls[BaseRenderingContext2D::FillPath], 1.0);
EXPECT_TRUE(context2d()->getUsage().boundingBoxPerimeterDrawCalls[BaseRenderingContext2D::FillText] > 0.0);
EXPECT_TRUE(context2d()->getUsage().boundingBoxPerimeterDrawCalls[BaseRenderingContext2D::StrokeText] == 0.0);
// create gradient
CanvasGradient* gradient;
gradient = context2d()->createLinearGradient(0, 0, 100, 100);
context2d()->setFillStyle(StringOrCanvasGradientOrCanvasPattern::fromCanvasGradient(gradient));
context2d()->fillRect(10, 10, 100, 20);
EXPECT_EQ(1, context2d()->getUsage().numGradients);
EXPECT_NEAR(100 * 20, context2d()->getUsage().boundingBoxAreaFillType[BaseRenderingContext2D::LinearGradientFillType], 1.0);
NonThrowableExceptionState exceptionState;
gradient = context2d()->createRadialGradient(0, 0, 100, 100, 200, 200, exceptionState);
context2d()->setFillStyle(StringOrCanvasGradientOrCanvasPattern::fromCanvasGradient(gradient));
context2d()->fillRect(10, 10, 100, 20);
EXPECT_EQ(2, context2d()->getUsage().numGradients);
EXPECT_NEAR(100 * 20, context2d()->getUsage().boundingBoxAreaFillType[BaseRenderingContext2D::RadialGradientFillType], 1.0);
// create pattern
NonThrowableExceptionState exceptionState2;
ExecutionContext* execCtx = canvasElement().getExecutionContext();
CanvasPattern* canvasPattern = context2d()->createPattern(execCtx, &m_opaqueBitmap, "repeat-x", exceptionState2);
StringOrCanvasGradientOrCanvasPattern pattern = StringOrCanvasGradientOrCanvasPattern::fromCanvasPattern(canvasPattern);
context2d()->setFillStyle(pattern);
context2d()->fillRect(10, 10, 200, 100);
EXPECT_EQ(1, context2d()->getUsage().numPatterns);
EXPECT_NEAR(200 * 100, context2d()->getUsage().boundingBoxAreaFillType[BaseRenderingContext2D::PatternFillType], 1.0);
// Other fill method
context2d()->fill(Path2D::create("Hello World!!!"));
EXPECT_EQ(numReps * 2 + 1, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillPath]);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokePath]);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokeText]);
EXPECT_EQ(0, context2d()->getUsage().numPutImageDataCalls);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::DrawImage]);
EXPECT_EQ(0, context2d()->getUsage().numGetImageDataCalls);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, StrokeTracking)
{
createContext(NonOpaque);
int numReps = 100;
for (int i = 0; i < numReps; i++) {
context2d()->strokeRect(10, 10, 100, 100);
context2d()->beginPath();
context2d()->moveTo(1, 1);
context2d()->lineTo(4, 1);
context2d()->lineTo(4, 4);
context2d()->lineTo(2, 2);
context2d()->lineTo(1, 4);
context2d()->stroke();
context2d()->strokeText("Hello World!!!", 10, 10, 1);
context2d()->strokeRect(10, 10, 100, 100);
context2d()->strokeText("Hello World!!!", 10, 10);
}
EXPECT_EQ(numReps, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokePath]);
EXPECT_EQ(numReps * 2, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokeRect]);
EXPECT_EQ(numReps * 2, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokeText]);
EXPECT_NEAR(static_cast<double>(numReps * (100 * 100) * 2), context2d()->getUsage().boundingBoxAreaDrawCalls[BaseRenderingContext2D::StrokeRect], 1.0);
EXPECT_NEAR(static_cast<double>(numReps * (2*100 + 2*100) * 2), context2d()->getUsage().boundingBoxPerimeterDrawCalls[BaseRenderingContext2D::StrokeRect], 1.0);
EXPECT_NEAR(static_cast<double>(numReps * (3 * 3)), context2d()->getUsage().boundingBoxAreaDrawCalls[BaseRenderingContext2D::StrokePath], 1.0);
EXPECT_NEAR(static_cast<double>(numReps * (2*3 + 2*3)), context2d()->getUsage().boundingBoxPerimeterDrawCalls[BaseRenderingContext2D::StrokePath], 1.0);
// create gradient
CanvasGradient* gradient;
gradient = context2d()->createLinearGradient(0, 0, 100, 100);
context2d()->setStrokeStyle(StringOrCanvasGradientOrCanvasPattern::fromCanvasGradient(gradient));
context2d()->strokeRect(10, 10, 100, 100);
EXPECT_EQ(1, context2d()->getUsage().numGradients);
EXPECT_NEAR(100 * 100, context2d()->getUsage().boundingBoxAreaFillType[BaseRenderingContext2D::LinearGradientFillType], 1.0);
// create pattern
NonThrowableExceptionState exceptionState;
ExecutionContext* execCtx = canvasElement().getExecutionContext();
CanvasPattern* canvasPattern = context2d()->createPattern(execCtx, &m_opaqueBitmap, "repeat-x", exceptionState);
StringOrCanvasGradientOrCanvasPattern pattern = StringOrCanvasGradientOrCanvasPattern::fromCanvasPattern(canvasPattern);
context2d()->setStrokeStyle(pattern);
context2d()->strokeRect(10, 10, 100, 100);
EXPECT_EQ(1, context2d()->getUsage().numPatterns);
EXPECT_NEAR(100 * 100, context2d()->getUsage().boundingBoxAreaFillType[BaseRenderingContext2D::PatternFillType], 1.0);
// Other stroke method
context2d()->stroke(Path2D::create("Hello World!!!"));
EXPECT_EQ(numReps + 1, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokePath]);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillPath]);
EXPECT_EQ(0, context2d()->getUsage().numNonConvexFillPathCalls);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillText]);
EXPECT_EQ(0, context2d()->getUsage().numPutImageDataCalls);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::DrawImage]);
EXPECT_EQ(0, context2d()->getUsage().numGetImageDataCalls);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, ImageTracking)
{
// Testing draw, put, get and filters
createContext(NonOpaque);
NonThrowableExceptionState exceptionState;
int numReps = 5;
int imgWidth = 100;
int imgHeight = 200;
for (int i = 0; i < numReps; i++) {
context2d()->putImageData(m_fullImageData.get(), 0, 0, exceptionState);
context2d()->drawImage(canvasElement().getExecutionContext(), &m_opaqueBitmap, 0, 0, 1, 1, 0, 0, imgWidth, imgHeight, exceptionState);
context2d()->getImageData(0, 0, 10, 100, exceptionState);
}
EXPECT_NEAR(numReps * imgWidth * imgHeight, context2d()->getUsage().boundingBoxAreaDrawCalls[BaseRenderingContext2D::DrawImage], 0.1);
EXPECT_NEAR(numReps * (2*imgWidth + 2*imgHeight), context2d()->getUsage().boundingBoxPerimeterDrawCalls[BaseRenderingContext2D::DrawImage], 0.1);
context2d()->setFilter("blur(5px)");
context2d()->drawImage(canvasElement().getExecutionContext(), &m_opaqueBitmap, 0, 0, 1, 1, 0, 0, 10, 10, exceptionState);
EXPECT_EQ(numReps, context2d()->getUsage().numPutImageDataCalls);
EXPECT_NE(0, context2d()->getUsage().areaPutImageDataCalls);
EXPECT_NEAR(numReps * m_fullImageData.get()->width() * m_fullImageData.get()->height(), context2d()->getUsage().areaPutImageDataCalls, 0.1);
EXPECT_EQ(numReps + 1, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::DrawImage]);
EXPECT_EQ(numReps, context2d()->getUsage().numGetImageDataCalls);
EXPECT_EQ(1, context2d()->getUsage().numFilters);
EXPECT_NEAR(numReps * 10 * 100, context2d()->getUsage().areaGetImageDataCalls, 0.1);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillPath]);
EXPECT_EQ(0, context2d()->getUsage().numNonConvexFillPathCalls);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillText]);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokePath]);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokeText]);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, ClearRectTracking)
{
createContext(NonOpaque);
context2d()->clearRect(0, 0, 1, 1);
context2d()->clearRect(0, 0, 2, 2);
EXPECT_EQ(2, context2d()->getUsage().numClearRectCalls);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillPath]);
EXPECT_EQ(0, context2d()->getUsage().numNonConvexFillPathCalls);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillText]);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokePath]);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::StrokeText]);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, ComplexClipsTracking)
{
createContext(NonOpaque);
context2d()->save();
// simple clip
context2d()->beginPath();
context2d()->moveTo(1, 1);
context2d()->lineTo(4, 1);
context2d()->lineTo(4, 4);
context2d()->lineTo(1, 4);
context2d()->clip("nonzero");
context2d()->fillRect(0, 0, 1, 1);
EXPECT_EQ(0, context2d()->getUsage().numDrawWithComplexClips);
// complex clips
int numReps = 5;
for (int i = 0; i < numReps; i++) {
context2d()->beginPath();
context2d()->moveTo(1, 1);
context2d()->lineTo(4, 1);
context2d()->lineTo(4, 4);
context2d()->lineTo(2, 2);
context2d()->lineTo(1, 4);
context2d()->clip("nonzero");
context2d()->fillRect(0, 0, 1, 1);
}
// remove all previous clips
context2d()->restore();
// simple clip again
context2d()->beginPath();
context2d()->moveTo(1, 1);
context2d()->lineTo(4, 1);
context2d()->lineTo(4, 4);
context2d()->lineTo(1, 4);
context2d()->clip("nonzero");
context2d()->fillRect(0, 0, 1, 1);
EXPECT_EQ(numReps, context2d()->getUsage().numDrawWithComplexClips);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, ShadowsTracking)
{
createContext(NonOpaque);
context2d()->beginPath();
context2d()->fillRect(0, 0, 1, 1);
// shadow with blur = 0
context2d()->beginPath();
context2d()->setShadowBlur(0.0);
context2d()->setShadowColor("rgba(255, 0, 0, 0.5)");
context2d()->setShadowOffsetX(1.0);
context2d()->setShadowOffsetY(1.0);
context2d()->fillRect(0, 0, 1, 1);
// shadow with alpha = 0
context2d()->beginPath();
context2d()->setShadowBlur(0.5);
context2d()->setShadowColor("rgba(255, 0, 0, 0)");
context2d()->setShadowOffsetX(1.0);
context2d()->setShadowOffsetY(1.0);
context2d()->fillRect(0, 0, 1, 1);
EXPECT_EQ(0, context2d()->getUsage().numBlurredShadows);
EXPECT_NEAR(0.0, context2d()->getUsage().boundingBoxAreaTimesShadowBlurSquared, 0.1);
EXPECT_NEAR(0.0, context2d()->getUsage().boundingBoxPerimeterTimesShadowBlurSquared, 0.1);
int numReps = 5;
double shadowBlur = 0.5;
for (int i = 0; i < numReps; i++) {
context2d()->beginPath();
context2d()->setShadowBlur(shadowBlur);
context2d()->setShadowColor("rgba(255, 0, 0, 0.5)");
context2d()->setShadowOffsetX(1.0);
context2d()->setShadowOffsetY(1.0);
context2d()->fillRect(0, 0, 3, 10);
}
EXPECT_EQ(numReps, context2d()->getUsage().numBlurredShadows);
double areaTimesShadowBlurSquared = shadowBlur * shadowBlur * numReps * 3 * 10;
double perimeterTimesShadowBlurSquared = shadowBlur * shadowBlur * numReps * (2*3 + 2*10);
EXPECT_NEAR(areaTimesShadowBlurSquared, context2d()->getUsage().boundingBoxAreaTimesShadowBlurSquared, 0.1);
EXPECT_NEAR(perimeterTimesShadowBlurSquared, context2d()->getUsage().boundingBoxPerimeterTimesShadowBlurSquared, 0.1);
double shadowBlur2 = 4.5;
context2d()->beginPath();
context2d()->setShadowBlur(shadowBlur2);
context2d()->moveTo(1, 1);
context2d()->lineTo(4, 1);
context2d()->lineTo(4, 4);
context2d()->lineTo(2, 2);
context2d()->lineTo(1, 4);
context2d()->fill();
areaTimesShadowBlurSquared += 3.0 * 3.0 * shadowBlur2 * shadowBlur2;
perimeterTimesShadowBlurSquared += (2*3 + 2*3) * shadowBlur2 * shadowBlur2;
EXPECT_NEAR(areaTimesShadowBlurSquared, context2d()->getUsage().boundingBoxAreaTimesShadowBlurSquared, 0.1);
EXPECT_NEAR(perimeterTimesShadowBlurSquared, context2d()->getUsage().boundingBoxPerimeterTimesShadowBlurSquared, 0.1);
NonThrowableExceptionState exceptionState;
int imgWidth = 100;
int imgHeight = 200;
for (int i = 0; i < numReps; i++) {
context2d()->putImageData(m_fullImageData.get(), 0, 0, exceptionState);
context2d()->setShadowBlur(shadowBlur2);
context2d()->drawImage(canvasElement().getExecutionContext(), &m_opaqueBitmap, 0, 0, 1, 1, 0, 0, imgWidth, imgHeight, exceptionState);
context2d()->getImageData(0, 0, 1, 1, exceptionState);
}
areaTimesShadowBlurSquared += imgWidth * imgHeight * shadowBlur2 * shadowBlur2 * numReps;
perimeterTimesShadowBlurSquared += (2*imgWidth + 2*imgHeight) * shadowBlur2 * shadowBlur2 * numReps;
EXPECT_NEAR(areaTimesShadowBlurSquared, context2d()->getUsage().boundingBoxAreaTimesShadowBlurSquared, 0.1);
EXPECT_NEAR(perimeterTimesShadowBlurSquared, context2d()->getUsage().boundingBoxPerimeterTimesShadowBlurSquared, 0.1);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, incrementFrameCount)
{
createContext(NonOpaque);
EXPECT_EQ(0, context2d()->getUsage().numFramesSinceReset);
context2d()->incrementFrameCount();
EXPECT_EQ(1, context2d()->getUsage().numFramesSinceReset);
context2d()->incrementFrameCount();
context2d()->incrementFrameCount();
EXPECT_EQ(3, context2d()->getUsage().numFramesSinceReset);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, resetUsageTracking)
{
createContext(NonOpaque);
EXPECT_EQ(0, context2d()->getUsage().numFramesSinceReset);
context2d()->incrementFrameCount();
context2d()->incrementFrameCount();
EXPECT_EQ(2, context2d()->getUsage().numFramesSinceReset);
const int numReps = 100;
for (int i = 0; i < numReps; i++) {
context2d()->fillRect(10, 10, 100, 100);
}
EXPECT_EQ(numReps, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillRect]);
context2d()->resetUsageTracking();
EXPECT_EQ(0, context2d()->getUsage().numFramesSinceReset);
EXPECT_EQ(0, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillRect]);
for (int i = 0; i < numReps; i++) {
context2d()->fillRect(10, 10, 100, 100);
}
EXPECT_EQ(numReps, context2d()->getUsage().numDrawCalls[BaseRenderingContext2D::FillRect]);
}
} // namespace UsageTrackingTests
} // namespace blink