Move textureop fallback code out of GrRTC and into AddTextureSetOps

(also renames CreateTextureSetOps to AddTextureSetOps, to match naming of
GrFillRectOp::AddFillRectOps).

Now that GrTextureOp can add more than one op to the GrRTC, it can take
over ownership of its fallback code for the texture set. It already had
taken over the code for non src-over blends when drawing a single texture.

Besides consolidating where the logic of converting TextureSetEntries into
op data lives, this makes the fallback case more consistent in terms of
performance. Previously, it would go through GrRTC::drawTexturedQuad,
which attempts to merge the clip with the draw for correctness reasons.
A batch never attempted these optimizations, so now even when one op per
quad is required, there won't be the overhead of comparing clips.

Change-Id: I30883e3bd45ed4386f81584e3d68229c46b17e47
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/255781
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2019-11-21 10:26:41 -05:00 committed by Skia Commit-Bot
parent 68e470de86
commit fe13ca3e67
4 changed files with 115 additions and 83 deletions

View File

@ -873,47 +873,15 @@ void GrRenderTargetContext::drawTextureSet(const GrClip& clip, const TextureSetE
SkDEBUGCODE(this->validate();)
GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextureSet", fContext);
if (mode != SkBlendMode::kSrcOver ||
!fContext->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
// Draw one at a time since the bulk API doesn't support non src-over blending, or the
// backend can't support the bulk geometry processor yet.
SkMatrix ctm;
for (int i = 0; i < cnt; ++i) {
float alpha = set[i].fAlpha;
ctm = viewMatrix;
if (set[i].fPreViewMatrix) {
ctm.preConcat(*set[i].fPreViewMatrix);
}
GrQuad quad, srcQuad;
if (set[i].fDstClipQuad) {
quad = GrQuad::MakeFromSkQuad(set[i].fDstClipQuad, ctm);
SkPoint srcPts[4];
GrMapRectPoints(set[i].fDstRect, set[i].fSrcRect, set[i].fDstClipQuad, srcPts, 4);
srcQuad = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
} else {
quad = GrQuad::MakeFromRect(set[i].fDstRect, ctm);
srcQuad = GrQuad(set[i].fSrcRect);
}
const SkRect* domain = constraint == SkCanvas::kStrict_SrcRectConstraint
? &set[i].fSrcRect : nullptr;
this->drawTexturedQuad(clip, set[i].fProxyView, set[i].fSrcColorType, texXform, filter,
{alpha, alpha, alpha, alpha}, mode, aa, set[i].fAAFlags, quad,
srcQuad, domain);
}
} else {
// 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;
GrTextureOp::CreateTextureSetOps(this, clip, fContext, set, cnt, filter, saturate, aaType,
constraint, viewMatrix, std::move(texXform));
}
// 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;
GrTextureOp::AddTextureSetOps(this, clip, fContext, set, cnt, filter, saturate, mode, aaType,
constraint, viewMatrix, std::move(texXform));
}
void GrRenderTargetContext::drawVertices(const GrClip& clip,

View File

@ -1054,19 +1054,56 @@ private:
};
// 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) {
void GrTextureOp::AddTextureSetOps(GrRenderTargetContext* rtc,
const GrClip& clip,
GrRecordingContext* context,
const GrRenderTargetContext::TextureSetEntry set[],
int cnt,
GrSamplerState::Filter filter,
Saturate saturate,
SkBlendMode blendMode,
GrAAType aaType,
SkCanvas::SrcRectConstraint constraint,
const SkMatrix& viewMatrix,
sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
// First check if we can support batches as a single op
if (blendMode != SkBlendMode::kSrcOver ||
!context->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
// Append each entry as its own op; these may still be GrTextureOps if the blend mode is
// src-over but the backend doesn't support dynamic state changes. Otherwise Make()
// automatically creates the appropriate GrFillRectOp to emulate GrTextureOp.
SkMatrix ctm;
for (int i = 0; i < cnt; ++i) {
float alpha = set[i].fAlpha;
ctm = viewMatrix;
if (set[i].fPreViewMatrix) {
ctm.preConcat(*set[i].fPreViewMatrix);
}
// First check if we can always just make a single op and avoid the extra iteration
GrQuad quad, srcQuad;
if (set[i].fDstClipQuad) {
quad = GrQuad::MakeFromSkQuad(set[i].fDstClipQuad, ctm);
SkPoint srcPts[4];
GrMapRectPoints(set[i].fDstRect, set[i].fSrcRect, set[i].fDstClipQuad, srcPts, 4);
srcQuad = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
} else {
quad = GrQuad::MakeFromRect(set[i].fDstRect, ctm);
srcQuad = GrQuad(set[i].fSrcRect);
}
const SkRect* domain = constraint == SkCanvas::kStrict_SrcRectConstraint
? &set[i].fSrcRect : nullptr;
auto op = Make(context, set[i].fProxyView, set[i].fSrcColorType, textureColorSpaceXform,
filter, {alpha, alpha, alpha, alpha}, saturate, blendMode, aaType,
set[i].fAAFlags, quad, srcQuad, domain);
rtc->addDrawOp(clip, std::move(op));
}
return;
}
// Second 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())) {

View File

@ -54,18 +54,20 @@ public:
const GrQuad& localQuad,
const SkRect* domain = nullptr);
// 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);
// Automatically falls back to using one GrFillRectOp per entry if dynamic states are not
// supported, or if the blend mode is not src-over.
static void AddTextureSetOps(GrRenderTargetContext*,
const GrClip& clip,
GrRecordingContext*,
const GrRenderTargetContext::TextureSetEntry[],
int cnt,
GrSamplerState::Filter,
Saturate,
SkBlendMode,
GrAAType,
SkCanvas::SrcRectConstraint,
const SkMatrix& viewMatrix,
sk_sp<GrColorSpaceXform> textureXform);
#if GR_TEST_UTILS
static uint32_t ClassID();

View File

@ -4,7 +4,7 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/core/SkBlendModePriv.h"
#include "src/gpu/GrClip.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrRenderTargetContext.h"
@ -35,12 +35,13 @@ sk_sp<GrSurfaceProxy> create_proxy(GrContext* context) {
typedef GrQuadAAFlags (*PerQuadAAFunc)(int i);
typedef void (*BulkRectTest)(skiatest::Reporter* reporter, GrContext* context,
PerQuadAAFunc perQuadAA, GrAAType overallAA,
PerQuadAAFunc perQuadAA, GrAAType overallAA, SkBlendMode blendMode,
int requestedTotNumQuads, int expectedNumOps);
//-------------------------------------------------------------------------------------------------
static void bulk_fill_rect_create_test(skiatest::Reporter* reporter, GrContext* context,
PerQuadAAFunc perQuadAA, GrAAType overallAA,
SkBlendMode blendMode,
int requestedTotNumQuads, int expectedNumOps) {
std::unique_ptr<GrRenderTargetContext> rtc = new_RTC(context);
@ -55,7 +56,7 @@ static void bulk_fill_rect_create_test(skiatest::Reporter* reporter, GrContext*
}
GrPaint paint;
paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
GrFillRectOp::AddFillRectOps(rtc.get(), GrNoClip(), context, std::move(paint), overallAA,
SkMatrix::I(), quads, requestedTotNumQuads);
@ -82,18 +83,22 @@ static void bulk_fill_rect_create_test(skiatest::Reporter* reporter, GrContext*
//-------------------------------------------------------------------------------------------------
static void bulk_texture_rect_create_test(skiatest::Reporter* reporter, GrContext* context,
PerQuadAAFunc perQuadAA, GrAAType overallAA,
SkBlendMode blendMode,
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());
sk_sp<GrSurfaceProxy> proxyA = create_proxy(context);
sk_sp<GrSurfaceProxy> proxyB = create_proxy(context);
GrSurfaceProxyView proxyViewA(std::move(proxyA), kTopLeft_GrSurfaceOrigin, GrSwizzle::RGBA());
GrSurfaceProxyView proxyViewB(std::move(proxyB), kTopLeft_GrSurfaceOrigin, GrSwizzle::RGBA());
auto set = new GrRenderTargetContext::TextureSetEntry[requestedTotNumQuads];
for (int i = 0; i < requestedTotNumQuads; ++i) {
set[i].fProxyView = proxyView;
// Alternate between two proxies to prevent op merging if the batch API was forced to submit
// one op at a time (to work, this does require that all fDstRects overlap).
set[i].fProxyView = i % 2 == 0 ? proxyViewA : proxyViewB;
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
@ -103,9 +108,10 @@ static void bulk_texture_rect_create_test(skiatest::Reporter* reporter, GrContex
set[i].fAAFlags = perQuadAA(i);
}
GrTextureOp::CreateTextureSetOps(rtc.get(), GrNoClip(), context, set, requestedTotNumQuads,
GrTextureOp::AddTextureSetOps(rtc.get(), GrNoClip(), context, set, requestedTotNumQuads,
GrSamplerState::Filter::kNearest,
GrTextureOp::Saturate::kYes,
blendMode,
overallAA,
SkCanvas::kStrict_SrcRectConstraint,
SkMatrix::I(), nullptr);
@ -115,9 +121,18 @@ static void bulk_texture_rect_create_test(skiatest::Reporter* reporter, GrContex
int actualTotNumQuads = 0;
if (blendMode != SkBlendMode::kSrcOver ||
!context->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
// In either of these two cases, GrTextureOp creates one op per quad instead. Since
// each entry alternates proxies but overlaps geometrically, this will prevent the ops
// from being merged back into fewer ops.
expectedNumOps = requestedTotNumQuads;
}
uint32_t expectedOpID = blendMode == SkBlendMode::kSrcOver ? GrTextureOp::ClassID()
: GrFillRectOp::ClassID();
for (int i = 0; i < actualNumOps; ++i) {
const GrOp* tmp = opsTask->getChain(i);
REPORTER_ASSERT(reporter, tmp->classID() == GrTextureOp::ClassID());
REPORTER_ASSERT(reporter, tmp->classID() == expectedOpID);
REPORTER_ASSERT(reporter, tmp->isChainTail());
actualTotNumQuads += ((GrDrawOp*) tmp)->numQuads();
}
@ -132,11 +147,6 @@ static void bulk_texture_rect_create_test(skiatest::Reporter* reporter, GrContex
//-------------------------------------------------------------------------------------------------
static void run_test(GrContext* context, skiatest::Reporter* reporter, BulkRectTest test) {
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 {
@ -145,7 +155,7 @@ static void run_test(GrContext* context, skiatest::Reporter* reporter, BulkRectT
static const int kNumExpectedOps = 2;
test(reporter, context, noAA, GrAAType::kNone,
test(reporter, context, noAA, GrAAType::kNone, SkBlendMode::kSrcOver,
2*GrResourceProvider::MaxNumNonAAQuads(), kNumExpectedOps);
}
@ -158,7 +168,7 @@ static void run_test(GrContext* context, skiatest::Reporter* reporter, BulkRectT
static const int kNumExpectedOps = 2;
test(reporter, context, noAA, GrAAType::kCoverage,
test(reporter, context, noAA, GrAAType::kCoverage, SkBlendMode::kSrcOver,
2*GrResourceProvider::MaxNumNonAAQuads(), kNumExpectedOps);
}
@ -172,7 +182,7 @@ static void run_test(GrContext* context, skiatest::Reporter* reporter, BulkRectT
int numExpectedOps = 2*GrResourceProvider::MaxNumNonAAQuads() /
GrResourceProvider::MaxNumAAQuads();
test(reporter, context, alternateAA, GrAAType::kCoverage,
test(reporter, context, alternateAA, GrAAType::kCoverage, SkBlendMode::kSrcOver,
2*GrResourceProvider::MaxNumNonAAQuads(), numExpectedOps);
}
@ -187,7 +197,22 @@ static void run_test(GrContext* context, skiatest::Reporter* reporter, BulkRectT
static const int kNumExpectedOps = 2;
test(reporter, context, runOfNonAA, GrAAType::kCoverage,
test(reporter, context, runOfNonAA, GrAAType::kCoverage, SkBlendMode::kSrcOver,
2*GrResourceProvider::MaxNumAAQuads(), kNumExpectedOps);
}
// In this case we use a blend mode other than src-over, which hits the GrFillRectOp fallback
// code path for GrTextureOp. We pass in the expected results if batching was successful, to
// that bulk_fill_rect_create_test batches on all modes; bulk_texture_rect_create_test is
// responsible for revising its expectations.
{
auto fixedAA = [](int i) -> GrQuadAAFlags {
return GrQuadAAFlags::kAll;
};
static const int kNumExpectedOps = 2;
test(reporter, context, fixedAA, GrAAType::kCoverage, SkBlendMode::kSrcATop,
2*GrResourceProvider::MaxNumAAQuads(), kNumExpectedOps);
}
}