Respect the max indexBuffer limits in the bulk texture draw API
This is required before we can lower the max AA quad count (again).
Bug: b/143572065 skia:9601
Change-Id: Id34123476ad49a57dc9ce7fe13f941c06f721b74
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/252603
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/gn/tests.gni b/gn/tests.gni
index b893768..f488058 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -24,6 +24,7 @@
"$_tests/BlendTest.cpp",
"$_tests/BlitMaskClip.cpp",
"$_tests/BlurTest.cpp",
+ "$_tests/BulkRectTest.cpp",
"$_tests/CTest.cpp",
"$_tests/CachedDataTest.cpp",
"$_tests/CachedDecodingPixelRefTest.cpp",
diff --git a/src/gpu/GrOpsTask.h b/src/gpu/GrOpsTask.h
index 1a4c517..fd69509 100644
--- a/src/gpu/GrOpsTask.h
+++ b/src/gpu/GrOpsTask.h
@@ -106,6 +106,11 @@
SkDEBUGCODE(int numClips() const override { return fNumClips; })
SkDEBUGCODE(void visitProxies_debugOnly(const VisitSurfaceProxyFunc&) const override;)
+#if GR_TEST_UTILS
+ int numOpChains() const { return fOpChains.count(); }
+ const GrOp* getChain(int index) const { return fOpChains[index].head(); }
+#endif
+
private:
bool isNoOp() const {
// TODO: GrLoadOp::kDiscard (i.e., storing a discard) should also be grounds for skipping
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 1e0cc19..41b3e60 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -903,15 +903,15 @@
srcQuad, domain);
}
} else {
- // Can use a single op, avoiding GrPaint creation, and can batch across proxies
+ // Create the minimum number of GrTextureOps needed to draw this set. Individual
+ // GrTextureOps can rebind the texture between draws thus avoiding GrPaint (re)creation.
AutoCheckFlush acf(this->drawingManager());
GrAAType aaType = this->chooseAAType(aa);
auto clampType = GrColorTypeClampType(this->colorInfo().colorType());
auto saturate = clampType == GrClampType::kManual ? GrTextureOp::Saturate::kYes
: GrTextureOp::Saturate::kNo;
- auto op = GrTextureOp::MakeSet(fContext, set, cnt, filter, saturate, aaType, constraint,
- viewMatrix, std::move(texXform));
- this->addDrawOp(clip, std::move(op));
+ GrTextureOp::CreateTextureSetOps(this, clip, fContext, set, cnt, filter, saturate, aaType,
+ constraint, viewMatrix, std::move(texXform));
}
}
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index eacff51..3543dbe 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -549,6 +549,7 @@
friend class GrTessellatingPathRenderer; // for access to add[Mesh]DrawOp
friend class GrCCPerFlushResources; // for access to addDrawOp
friend class GrCoverageCountingPathRenderer; // for access to addDrawOp
+ friend class GrTextureOp; // for access to addDrawOp
// for a unit test
friend void test_draw_op(GrContext*,
GrRenderTargetContext*,
diff --git a/src/gpu/ops/GrDrawOp.h b/src/gpu/ops/GrDrawOp.h
index a9e9752..f366a3b 100644
--- a/src/gpu/ops/GrDrawOp.h
+++ b/src/gpu/ops/GrDrawOp.h
@@ -54,6 +54,11 @@
}
#endif
+#if GR_TEST_UTILS
+ // This is really only intended for GrTextureOp to override
+ virtual int numQuads() const { return -1; }
+#endif
+
private:
typedef GrOp INHERITED;
};
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp
index 86fe00f..8048f20 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.cpp
+++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp
@@ -166,6 +166,18 @@
}
}
+#ifdef SK_DEBUG
+int QuadLimit(IndexBufferOption option) {
+ switch (option) {
+ case IndexBufferOption::kPictureFramed: return GrResourceProvider::MaxNumAAQuads();
+ case IndexBufferOption::kIndexedRects: return GrResourceProvider::MaxNumNonAAQuads();
+ case IndexBufferOption::kTriStrips: return SK_MaxS32; // not limited by an indexBuffer
+ }
+
+ SkUNREACHABLE;
+}
+#endif
+
void ConfigureMesh(GrMesh* mesh, const VertexSpec& spec,
int runningQuadCount, int quadsInDraw, int maxVerts,
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.h b/src/gpu/ops/GrQuadPerEdgeAA.h
index ae245ea..5f6f21e 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.h
+++ b/src/gpu/ops/GrQuadPerEdgeAA.h
@@ -151,6 +151,11 @@
// It will, correctly, return nullptr if the indexBufferOption is kTriStrips.
sk_sp<const GrBuffer> GetIndexBuffer(GrMeshDrawOp::Target*, IndexBufferOption);
+#ifdef SK_DEBUG
+ // What is the maximum number of quads allowed for the specified indexBuffer option?
+ int QuadLimit(IndexBufferOption);
+#endif
+
// This method will configure the vertex and index data of the provided 'mesh' to comply
// with the indexing method specified in the vertexSpec. It is up to the calling code
// to allocate and fill in the vertex data and acquire the correct indexBuffer if it is needed.
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 7c19c4e..50d20c1 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -159,6 +159,7 @@
color, saturate, aaType, aaFlags, deviceQuad, localQuad,
domain);
}
+
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
const GrRenderTargetContext::TextureSetEntry set[],
int cnt,
@@ -595,9 +596,11 @@
auto textureType = fViewCountPairs[0].fProxyView.asTextureProxy()->textureType();
GrAAType aaType = this->aaType();
+ int quadCount = 0;
for (const auto& op : ChainRange<TextureOp>(this)) {
for (unsigned p = 0; p < op.fProxyCnt; ++p) {
auto* proxy = op.fViewCountPairs[p].fProxyView.asTextureProxy();
+ quadCount += op.fViewCountPairs[p].fQuadCnt;
SkASSERT(proxy);
SkASSERT(proxy->textureType() == textureType);
SkASSERT(op.fViewCountPairs[p].fProxyView.swizzle() ==
@@ -612,9 +615,15 @@
SkASSERT(aaType == GrAAType::kMSAA && op.aaType() == GrAAType::kMSAA);
}
}
+
+ SkASSERT(quadCount == this->numChainedQuads());
}
#endif
+#if GR_TEST_UTILS
+ int numQuads() const final { return this->totNumQuads(); }
+#endif
+
void characterize(PrePreparedDesc* desc) const {
GrQuad::Type quadType = GrQuad::Type::kAxisAligned;
ColorType colorType = ColorType::kNone;
@@ -659,6 +668,8 @@
desc->fVertexSpec = VertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true,
domain, overallAAType, /* alpha as coverage */ true,
indexBufferOption);
+
+ SkASSERT(desc->fNumTotalQuads <= GrQuadPerEdgeAA::QuadLimit(indexBufferOption));
}
int totNumQuads() const {
@@ -879,21 +890,25 @@
} // anonymous namespace
-namespace GrTextureOp {
+#if GR_TEST_UTILS
+uint32_t GrTextureOp::ClassID() {
+ return TextureOp::ClassID();
+}
+#endif
-std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
- GrSurfaceProxyView proxyView,
- GrColorType srcColorType,
- sk_sp<GrColorSpaceXform> textureXform,
- GrSamplerState::Filter filter,
- const SkPMColor4f& color,
- Saturate saturate,
- SkBlendMode blendMode,
- GrAAType aaType,
- GrQuadAAFlags aaFlags,
- const GrQuad& deviceQuad,
- const GrQuad& localQuad,
- const SkRect* domain) {
+std::unique_ptr<GrDrawOp> GrTextureOp::Make(GrRecordingContext* context,
+ GrSurfaceProxyView proxyView,
+ GrColorType srcColorType,
+ sk_sp<GrColorSpaceXform> textureXform,
+ GrSamplerState::Filter filter,
+ const SkPMColor4f& color,
+ Saturate saturate,
+ SkBlendMode blendMode,
+ GrAAType aaType,
+ GrQuadAAFlags aaFlags,
+ const GrQuad& deviceQuad,
+ const GrQuad& localQuad,
+ const SkRect* domain) {
GrTextureProxy* proxy = proxyView.asTextureProxy();
// Apply optimizations that are valid whether or not using GrTextureOp or GrFillRectOp
if (domain && domain->contains(proxy->backingStoreBoundsRect())) {
@@ -938,20 +953,143 @@
}
}
-std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext* context,
- const GrRenderTargetContext::TextureSetEntry set[],
- int cnt,
- GrSamplerState::Filter filter,
- Saturate saturate,
- GrAAType aaType,
- SkCanvas::SrcRectConstraint constraint,
- const SkMatrix& viewMatrix,
- sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
- return TextureOp::Make(context, set, cnt, filter, saturate, aaType, constraint, viewMatrix,
- std::move(textureColorSpaceXform));
-}
+// A helper class that assists in breaking up bulk API quad draws into manageable chunks.
+class GrTextureOp::BatchSizeLimiter {
+public:
+ BatchSizeLimiter(GrRenderTargetContext* rtc,
+ const GrClip& clip,
+ GrRecordingContext* context,
+ int numEntries,
+ GrSamplerState::Filter filter,
+ GrTextureOp::Saturate saturate,
+ SkCanvas::SrcRectConstraint constraint,
+ const SkMatrix& viewMatrix,
+ sk_sp<GrColorSpaceXform> textureColorSpaceXform)
+ : fRTC(rtc)
+ , fClip(clip)
+ , fContext(context)
+ , fFilter(filter)
+ , fSaturate(saturate)
+ , fConstraint(constraint)
+ , fViewMatrix(viewMatrix)
+ , fTextureColorSpaceXform(textureColorSpaceXform)
+ , fNumLeft(numEntries) {
+ }
-} // namespace GrTextureOp
+ void createOp(const GrRenderTargetContext::TextureSetEntry set[],
+ int clumpSize,
+ GrAAType aaType) {
+ std::unique_ptr<GrDrawOp> op = TextureOp::Make(fContext, &set[fNumClumped], clumpSize,
+ fFilter, fSaturate, aaType,
+ fConstraint, fViewMatrix,
+ fTextureColorSpaceXform);
+ fRTC->addDrawOp(fClip, std::move(op));
+
+ fNumLeft -= clumpSize;
+ fNumClumped += clumpSize;
+ }
+
+ int numLeft() const { return fNumLeft; }
+ int baseIndex() const { return fNumClumped; }
+
+private:
+ GrRenderTargetContext* fRTC;
+ const GrClip& fClip;
+ GrRecordingContext* fContext;
+ GrSamplerState::Filter fFilter;
+ GrTextureOp::Saturate fSaturate;
+ SkCanvas::SrcRectConstraint fConstraint;
+ const SkMatrix& fViewMatrix;
+ sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
+
+ int fNumLeft;
+ int fNumClumped = 0; // also the offset for the start of the next clump
+};
+
+// Greedily clump quad draws together until the index buffer limit is exceeded.
+void GrTextureOp::CreateTextureSetOps(GrRenderTargetContext* rtc,
+ const GrClip& clip,
+ GrRecordingContext* context,
+ const GrRenderTargetContext::TextureSetEntry set[],
+ int cnt,
+ GrSamplerState::Filter filter,
+ Saturate saturate,
+ GrAAType aaType,
+ SkCanvas::SrcRectConstraint constraint,
+ const SkMatrix& viewMatrix,
+ sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
+
+ // First check if we can always just make a single op and avoid the extra iteration
+ // needed to clump things together.
+ if (cnt <= SkTMin(GrResourceProvider::MaxNumNonAAQuads(),
+ GrResourceProvider::MaxNumAAQuads())) {
+ auto op = TextureOp::Make(context, set, cnt, filter, saturate, aaType,
+ constraint, viewMatrix, std::move(textureColorSpaceXform));
+ rtc->addDrawOp(clip, std::move(op));
+ return;
+ }
+
+ BatchSizeLimiter state(rtc, clip, context, cnt, filter, saturate, constraint, viewMatrix,
+ std::move(textureColorSpaceXform));
+
+ // kNone and kMSAA never get altered
+ if (aaType == GrAAType::kNone || aaType == GrAAType::kMSAA) {
+ // Clump these into series of MaxNumNonAAQuads-sized GrTextureOps
+ while (state.numLeft() > 0) {
+ int clumpSize = SkTMin(state.numLeft(), GrResourceProvider::MaxNumNonAAQuads());
+
+ state.createOp(set, clumpSize, aaType);
+ }
+ } else {
+ // kCoverage can be downgraded to kNone. Note that the following is conservative. kCoverage
+ // can also get downgraded to kNone if all the quads are on integer coordinates and
+ // axis-aligned.
+ SkASSERT(aaType == GrAAType::kCoverage);
+
+ while (state.numLeft() > 0) {
+ GrAAType runningAA = GrAAType::kNone;
+ bool clumped = false;
+
+ for (int i = 0; i < state.numLeft(); ++i) {
+ int absIndex = state.baseIndex() + i;
+
+ if (set[absIndex].fAAFlags != GrQuadAAFlags::kNone) {
+
+ if (i >= GrResourceProvider::MaxNumAAQuads()) {
+ // Here we either need to boost the AA type to kCoverage, but doing so with
+ // all the accumulated quads would overflow, or we have a set of AA quads
+ // that has just gotten too large. In either case, calve off the existing
+ // quads as their own TextureOp.
+ state.createOp(
+ set,
+ runningAA == GrAAType::kNone ? i : GrResourceProvider::MaxNumAAQuads(),
+ runningAA); // maybe downgrading AA here
+ clumped = true;
+ break;
+ }
+
+ runningAA = GrAAType::kCoverage;
+ } else if (runningAA == GrAAType::kNone) {
+
+ if (i >= GrResourceProvider::MaxNumNonAAQuads()) {
+ // Here we've found a consistent batch of non-AA quads that has gotten too
+ // large. Calve it off as its own GrTextureOp.
+ state.createOp(set, GrResourceProvider::MaxNumNonAAQuads(),
+ GrAAType::kNone); // definitely downgrading AA here
+ clumped = true;
+ break;
+ }
+ }
+ }
+
+ if (!clumped) {
+ // We ran through the above loop w/o hitting a limit. Spit out this last clump of
+ // quads and call it a day.
+ state.createOp(set, state.numLeft(), runningAA); // maybe downgrading AA here
+ }
+ }
+ }
+}
#if GR_TEST_UTILS
#include "include/private/GrRecordingContext.h"
diff --git a/src/gpu/ops/GrTextureOp.h b/src/gpu/ops/GrTextureOp.h
index a4eb15b..600bcc8 100644
--- a/src/gpu/ops/GrTextureOp.h
+++ b/src/gpu/ops/GrTextureOp.h
@@ -20,49 +20,59 @@
struct SkRect;
class SkMatrix;
-namespace GrTextureOp {
+class GrTextureOp {
+public:
-/**
- * Controls whether saturate() is called after the texture is color-converted to ensure all
- * color values are in 0..1 range.
- */
-enum class Saturate : bool { kNo = false, kYes = true };
+ /**
+ * Controls whether saturate() is called after the texture is color-converted to ensure all
+ * color values are in 0..1 range.
+ */
+ enum class Saturate : bool { kNo = false, kYes = true };
-/**
- * Creates an op that draws a sub-quadrilateral of a texture. The passed color is modulated by the
- * texture's color. 'deviceQuad' specifies the device-space coordinates to draw, using 'localQuad'
- * to map into the proxy's texture space. If non-null, 'domain' represents the boundary for the
- * strict src rect constraint. If GrAAType is kCoverage then AA is applied to the edges
- * indicated by GrQuadAAFlags. Otherwise, GrQuadAAFlags is ignored.
- *
- * This is functionally very similar to GrFillRectOp::Make, except that the GrPaint has been
- * deconstructed into the texture, filter, modulating color, and blend mode. When blend mode is
- * src over, this will return a GrFillRectOp with a paint that samples the proxy.
- */
-std::unique_ptr<GrDrawOp> Make(GrRecordingContext*,
- GrSurfaceProxyView,
- GrColorType srcColorType,
- sk_sp<GrColorSpaceXform>,
- GrSamplerState::Filter,
- const SkPMColor4f&,
- Saturate,
- SkBlendMode,
- GrAAType,
- GrQuadAAFlags,
- const GrQuad& deviceQuad,
- const GrQuad& localQuad,
- const SkRect* domain = nullptr);
+ /**
+ * Creates an op that draws a sub-quadrilateral of a texture. The passed color is modulated by
+ * the texture's color. 'deviceQuad' specifies the device-space coordinates to draw, using
+ * 'localQuad' to map into the proxy's texture space. If non-null, 'domain' represents the
+ * boundary for the strict src rect constraint. If GrAAType is kCoverage then AA is applied to
+ * the edges indicated by GrQuadAAFlags. Otherwise, GrQuadAAFlags is ignored.
+ *
+ * This is functionally very similar to GrFillRectOp::Make, except that the GrPaint has been
+ * deconstructed into the texture, filter, modulating color, and blend mode. When blend mode is
+ * src over, this will return a GrFillRectOp with a paint that samples the proxy.
+ */
+ static std::unique_ptr<GrDrawOp> Make(GrRecordingContext*,
+ GrSurfaceProxyView,
+ GrColorType srcColorType,
+ sk_sp<GrColorSpaceXform>,
+ GrSamplerState::Filter,
+ const SkPMColor4f&,
+ Saturate,
+ SkBlendMode,
+ GrAAType,
+ GrQuadAAFlags,
+ const GrQuad& deviceQuad,
+ const GrQuad& localQuad,
+ const SkRect* domain = nullptr);
-// Unlike the single-proxy factory, this only supports src-over blending.
-std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext*,
- const GrRenderTargetContext::TextureSetEntry[],
- int cnt,
- GrSamplerState::Filter,
- Saturate,
- GrAAType,
- SkCanvas::SrcRectConstraint,
- const SkMatrix& viewMatrix,
- sk_sp<GrColorSpaceXform> textureXform);
+ // Unlike the single-proxy factory, this only supports src-over blending.
+ static void CreateTextureSetOps(GrRenderTargetContext*,
+ const GrClip& clip,
+ GrRecordingContext*,
+ const GrRenderTargetContext::TextureSetEntry[],
+ int cnt,
+ GrSamplerState::Filter,
+ Saturate,
+ GrAAType,
+ SkCanvas::SrcRectConstraint,
+ const SkMatrix& viewMatrix,
+ sk_sp<GrColorSpaceXform> textureXform);
-}
+#if GR_TEST_UTILS
+ static uint32_t ClassID();
+#endif
+
+private:
+ class BatchSizeLimiter;
+};
+
#endif // GrTextureOp_DEFINED
diff --git a/tests/BulkRectTest.cpp b/tests/BulkRectTest.cpp
new file mode 100644
index 0000000..b89ccc9
--- /dev/null
+++ b/tests/BulkRectTest.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/GrClip.h"
+#include "src/gpu/GrContextPriv.h"
+#include "src/gpu/GrMemoryPool.h"
+#include "src/gpu/GrRenderTargetContext.h"
+#include "src/gpu/ops/GrTextureOp.h"
+#include "tests/Test.h"
+
+static std::unique_ptr<GrRenderTargetContext> new_RTC(GrContext* context) {
+ return context->priv().makeDeferredRenderTargetContext(SkBackingFit::kExact, 128, 128,
+ GrColorType::kRGBA_8888, nullptr);
+}
+
+sk_sp<GrSurfaceProxy> create_proxy(GrContext* context) {
+ GrSurfaceDesc desc;
+ desc.fConfig = kRGBA_8888_GrPixelConfig;
+ desc.fWidth = 128;
+ desc.fHeight = 128;
+
+ const GrBackendFormat format = context->priv().caps()->getDefaultBackendFormat(
+ GrColorType::kRGBA_8888,
+ GrRenderable::kYes);
+
+ return context->priv().proxyProvider()->createProxy(
+ format, desc, GrRenderable::kYes, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
+ SkBackingFit::kExact, SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone);
+}
+
+typedef GrQuadAAFlags (*GimmeSomeAAFunc)(int i);
+
+static void bulk_rect_create_test(skiatest::Reporter* reporter, GrContext* context,
+ GimmeSomeAAFunc gimmeSomeAA, GrAAType overallAA,
+ int requestedTotNumQuads, int expectedNumOps) {
+
+ std::unique_ptr<GrRenderTargetContext> rtc = new_RTC(context);
+
+ sk_sp<GrSurfaceProxy> proxy = create_proxy(context);
+
+ GrSurfaceProxyView proxyView(std::move(proxy), kTopLeft_GrSurfaceOrigin, GrSwizzle::RGBA());
+
+ auto set = new GrRenderTargetContext::TextureSetEntry[requestedTotNumQuads];
+
+ for (int i = 0; i < requestedTotNumQuads; ++i) {
+ set[i].fProxyView = proxyView;
+ set[i].fSrcColorType = GrColorType::kRGBA_8888;
+ set[i].fSrcRect = SkRect::MakeWH(100.0f, 100.0f);
+ set[i].fDstRect = SkRect::MakeWH(100.5f, 100.5f); // prevent the int non-AA optimization
+ set[i].fDstClipQuad = nullptr;
+ set[i].fPreViewMatrix = nullptr;
+ set[i].fAlpha = 1.0f;
+ set[i].fAAFlags = gimmeSomeAA(i);
+ }
+
+ GrTextureOp::CreateTextureSetOps(rtc.get(), GrNoClip(), context, set, requestedTotNumQuads,
+ GrSamplerState::Filter::kNearest,
+ GrTextureOp::Saturate::kYes,
+ overallAA,
+ SkCanvas::kStrict_SrcRectConstraint,
+ SkMatrix::I(), nullptr);
+
+ GrOpsTask* opsTask = rtc->testingOnly_PeekLastOpsTask();
+ int actualNumOps = opsTask->numOpChains();
+
+ int actualTotNumQuads = 0;
+
+ for (int i = 0; i < actualNumOps; ++i) {
+ const GrOp* tmp = opsTask->getChain(i);
+ REPORTER_ASSERT(reporter, tmp->classID() == GrTextureOp::ClassID());
+ REPORTER_ASSERT(reporter, tmp->isChainTail());
+ actualTotNumQuads += ((GrDrawOp*) tmp)->numQuads();
+ }
+
+ REPORTER_ASSERT(reporter, expectedNumOps == actualNumOps);
+ REPORTER_ASSERT(reporter, requestedTotNumQuads == actualTotNumQuads);
+
+ context->flush();
+
+ delete[] set;
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BulkRect, reporter, ctxInfo) {
+ GrContext* context = ctxInfo.grContext();
+
+ if (!context->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
+ return;
+ }
+
+ // This is the simple case where there is no AA at all. We expect 2 non-AA clumps of quads.
+ {
+ auto noAA = [](int i) -> GrQuadAAFlags {
+ return GrQuadAAFlags::kNone;
+ };
+
+ static const int kNumExpectedOps = 2;
+
+ bulk_rect_create_test(reporter, context, noAA, GrAAType::kNone,
+ 2*GrResourceProvider::MaxNumNonAAQuads(), kNumExpectedOps);
+ }
+
+ // This is the same as the above case except the overall AA is kCoverage. However, since
+ // the per-quad AA is still none, all the quads should be downgraded to non-AA.
+ {
+ auto noAA = [](int i) -> GrQuadAAFlags {
+ return GrQuadAAFlags::kNone;
+ };
+
+ static const int kNumExpectedOps = 2;
+
+ bulk_rect_create_test(reporter, context, noAA, GrAAType::kCoverage,
+ 2*GrResourceProvider::MaxNumNonAAQuads(), kNumExpectedOps);
+ }
+
+ // This case has an overall AA of kCoverage but the per-quad AA alternates.
+ // We should end up with several aa-sized clumps
+ {
+ auto alternateAA = [](int i) -> GrQuadAAFlags {
+ return (i % 2) ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
+ };
+
+ int numExpectedOps = 2*GrResourceProvider::MaxNumNonAAQuads() /
+ GrResourceProvider::MaxNumAAQuads();
+
+ bulk_rect_create_test(reporter, context, alternateAA, GrAAType::kCoverage,
+ 2*GrResourceProvider::MaxNumNonAAQuads(), numExpectedOps);
+ }
+
+ // In this case we have a run of MaxNumAAQuads non-AA quads and then AA quads. This
+ // exercises the case where we have a clump of quads that can't be upgraded to AA bc of
+ // its size. We expect one clump of non-AA quads followed by one clump of AA quads.
+ {
+ auto runOfNonAA = [](int i) -> GrQuadAAFlags {
+ return (i < GrResourceProvider::MaxNumAAQuads()) ? GrQuadAAFlags::kNone
+ : GrQuadAAFlags::kAll;
+ };
+
+ static const int kNumExpectedOps = 2;
+
+ bulk_rect_create_test(reporter, context, runOfNonAA, GrAAType::kCoverage,
+ 2*GrResourceProvider::MaxNumAAQuads(), kNumExpectedOps);
+ }
+}