Start disentangling tessellation from pipeline creation in GrTextureOp

The idea/dream here is to be able to (optionally) tessellate in onPrePrepareDraws and then just copy the prePrepared data into the vertex buffer.

Change -Id: I899eebce36dc9516bfc4b0fce5e657458167c772

Change-Id: I899eebce36dc9516bfc4b0fce5e657458167c772
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/248804
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2019-10-16 09:20:25 -04:00 committed by Skia Commit-Bot
parent ebc89c1a7d
commit 29f3854d4e
9 changed files with 157 additions and 69 deletions

View File

@ -578,7 +578,7 @@ void GrDrawingManager::moveRenderTasksToDDL(SkDeferredDisplayList* ddl) {
fDAG.swap(&ddl->fRenderTasks);
for (auto renderTask : ddl->fRenderTasks) {
renderTask->prePrepare();
renderTask->prePrepare(fContext);
}
if (fPathRendererChain) {

View File

@ -391,7 +391,7 @@ void GrOpsTask::endFlush() {
fAuditTrail = nullptr;
}
void GrOpsTask::onPrePrepare() {
void GrOpsTask::onPrePrepare(GrRecordingContext* context) {
SkASSERT(this->isClosed());
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
@ -406,7 +406,7 @@ void GrOpsTask::onPrePrepare() {
for (const auto& chain : fOpChains) {
if (chain.shouldExecute()) {
chain.head()->prePrepare();
chain.head()->prePrepare(context);
}
}
}

View File

@ -50,7 +50,7 @@ public:
*/
void endFlush() override;
void onPrePrepare() override;
void onPrePrepare(GrRecordingContext*) override;
/**
* Together these two functions flush all queued up draws to GrCommandBuffer. The return value
* of executeOps() indicates whether any commands were actually issued to the GPU.

View File

@ -29,7 +29,7 @@ public:
void makeClosed(const GrCaps&);
void prePrepare() { this->onPrePrepare(); }
void prePrepare(GrRecordingContext* context) { this->onPrePrepare(context); }
// These two methods are only invoked at flush time
void prepare(GrOpFlushState* flushState);
@ -185,7 +185,8 @@ private:
}
};
virtual void onPrePrepare() {} // Only the GrOpsTask currently overrides this virtual
// Only the GrOpsTask currently overrides this virtual
virtual void onPrePrepare(GrRecordingContext*) {}
virtual void onPrepare(GrOpFlushState*) {} // Only the GrOpsTask overrides this virtual
virtual bool onExecute(GrOpFlushState* flushState) = 0;

View File

@ -72,10 +72,12 @@ protected:
};
private:
void onPrePrepare() final { this->onPrePrepareDraws(); }
void onPrePrepare(GrRecordingContext* context) final { this->onPrePrepareDraws(context); }
void onPrepare(GrOpFlushState* state) final;
virtual void onPrePrepareDraws() {} // Only the GrTextureOp currently overrides this virtual
// Only the GrTextureOp currently overrides this virtual
virtual void onPrePrepareDraws(GrRecordingContext*) {}
virtual void onPrepareDraws(Target*) = 0;
typedef GrDrawOp INHERITED;
};

View File

@ -158,7 +158,7 @@ public:
* onPrePrepare must be prepared to handle both cases (when onPrePrepare has been called
* ahead of time and when it has not been called).
*/
void prePrepare() { this->onPrePrepare(); }
void prePrepare(GrRecordingContext* context) { this->onPrePrepare(context); }
/**
* Called prior to executing. The op should perform any resource creation or data transfers
@ -289,7 +289,8 @@ private:
return CombineResult::kCannotCombine;
}
virtual void onPrePrepare() {} // Only GrMeshDrawOp currently overrides this virtual
// Only GrMeshDrawOp currently overrides this virtual
virtual void onPrePrepare(GrRecordingContext*) {}
virtual void onPrepare(GrOpFlushState*) = 0;
// If this op is chained then chainBounds is the union of the bounds of all ops in the chain.
// Otherwise, this op's bounds.

View File

@ -477,32 +477,10 @@ static V4f compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags, Verti
return coverage;
}
enum class CoverageMode {
kNone,
kWithPosition,
kWithColor
};
static CoverageMode get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec& spec) {
if (spec.usesCoverageAA()) {
if (spec.compatibleWithCoverageAsAlpha() && spec.hasVertexColors() &&
!spec.requiresGeometryDomain()) {
// Using a geometric domain acts as a second source of coverage and folding the original
// coverage into color makes it impossible to apply the color's alpha to the geometric
// domain's coverage when the original shape is clipped.
return CoverageMode::kWithColor;
} else {
return CoverageMode::kWithPosition;
}
} else {
return CoverageMode::kNone;
}
}
// Writes four vertices in triangle strip order, including the additional data for local
// coordinates, geometry + texture domains, color, and coverage as needed to satisfy the vertex spec
static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
CoverageMode mode, const V4f& coverage, SkPMColor4f color4f,
GrQuadPerEdgeAA::CoverageMode mode, const V4f& coverage, SkPMColor4f color4f,
const SkRect& geomDomain, const SkRect& texDomain, const Vertices& quad) {
static constexpr auto If = GrVertexWriter::If<float>;
@ -511,13 +489,14 @@ static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& sp
// perspective and coverage mode.
vb->write(quad.fX[i], quad.fY[i],
If(spec.deviceQuadType() == GrQuad::Type::kPerspective, quad.fW[i]),
If(mode == CoverageMode::kWithPosition, coverage[i]));
If(mode == GrQuadPerEdgeAA::CoverageMode::kWithPosition, coverage[i]));
// save color
if (spec.hasVertexColors()) {
bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
vb->write(GrVertexColor(
color4f * (mode == CoverageMode::kWithColor ? coverage[i] : 1.f), wide));
color4f * (mode == GrQuadPerEdgeAA::CoverageMode::kWithColor ? coverage[i] : 1.f),
wide));
}
// save local position
@ -584,7 +563,7 @@ void* Tessellate(void* vertices, const VertexSpec& spec, const GrQuad& deviceQua
SkASSERT(deviceQuad.quadType() <= spec.deviceQuadType());
SkASSERT(!spec.hasLocalCoords() || localQuad.quadType() <= spec.localQuadType());
CoverageMode mode = get_mode_for_spec(spec);
GrQuadPerEdgeAA::CoverageMode mode = spec.coverageMode();
// Load position data into V4fs (always x, y, and load w to avoid branching down the road)
Vertices outer;
@ -685,6 +664,63 @@ int VertexSpec::localDimensionality() const {
return fHasLocalCoords ? (this->localQuadType() == GrQuad::Type::kPerspective ? 3 : 2) : 0;
}
CoverageMode VertexSpec::coverageMode() const {
if (this->usesCoverageAA()) {
if (this->compatibleWithCoverageAsAlpha() && this->hasVertexColors() &&
!this->requiresGeometryDomain()) {
// Using a geometric domain acts as a second source of coverage and folding
// the original coverage into color makes it impossible to apply the color's
// alpha to the geometric domain's coverage when the original shape is clipped.
return CoverageMode::kWithColor;
} else {
return CoverageMode::kWithPosition;
}
} else {
return CoverageMode::kNone;
}
}
// This needs to stay in sync w/ QuadPerEdgeAAGeometryProcessor::initializeAttrs
size_t VertexSpec::vertexSize() const {
bool needsPerspective = (this->deviceDimensionality() == 3);
CoverageMode coverageMode = this->coverageMode();
size_t count = 0;
if (coverageMode == CoverageMode::kWithPosition) {
if (needsPerspective) {
count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
} else {
count += GrVertexAttribTypeSize(kFloat2_GrVertexAttribType) +
GrVertexAttribTypeSize(kFloat_GrVertexAttribType);
}
} else {
if (needsPerspective) {
count += GrVertexAttribTypeSize(kFloat3_GrVertexAttribType);
} else {
count += GrVertexAttribTypeSize(kFloat2_GrVertexAttribType);
}
}
if (this->requiresGeometryDomain()) {
count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
}
count += this->localDimensionality() * GrVertexAttribTypeSize(kFloat_GrVertexAttribType);
if (ColorType::kByte == this->colorType()) {
count += GrVertexAttribTypeSize(kUByte4_norm_GrVertexAttribType);
} else if (ColorType::kHalf == this->colorType()) {
count += GrVertexAttribTypeSize(kHalf4_GrVertexAttribType);
}
if (this->hasDomain()) {
count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
}
return count;
}
////////////////// Geometry Processor Implementation
class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
@ -918,9 +954,10 @@ private:
this->setTextureSamplerCnt(1);
}
// This needs to stay in sync w/ VertexSpec::vertexSize
void initializeAttrs(const VertexSpec& spec) {
fNeedsPerspective = spec.deviceDimensionality() == 3;
fCoverageMode = get_mode_for_spec(spec);
fCoverageMode = spec.coverageMode();
if (fCoverageMode == CoverageMode::kWithPosition) {
if (fNeedsPerspective) {

View File

@ -25,6 +25,7 @@ class GrShaderCaps;
namespace GrQuadPerEdgeAA {
using Saturate = GrTextureOp::Saturate;
enum class CoverageMode { kNone, kWithPosition, kWithColor };
enum class Domain : bool { kNo = false, kYes = true };
enum class ColorType { kNone, kByte, kHalf, kLast = kHalf };
static const int kColorTypeCount = static_cast<int>(ColorType::kLast) + 1;
@ -65,6 +66,10 @@ namespace GrQuadPerEdgeAA {
int localDimensionality() const;
int verticesPerQuad() const { return fUsesCoverageAA ? 8 : 4; }
CoverageMode coverageMode() const;
size_t vertexSize() const;
private:
static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type doesn't fit in 2 bits");
static_assert(kColorTypeCount <= 4, "Color doesn't fit in 2 bits");

View File

@ -427,24 +427,50 @@ private:
}
}
void onPrePrepareDraws() override {
void onPrePrepareDraws(GrRecordingContext* context) override {
SkASSERT(!fPrePrepared);
// Pull forward the tessellation of the quads to here
//GrOpMemoryPool* pool = context->priv().opMemoryPool();
fPrePrepared = true;
}
// onPrePrepareDraws may or may not have been called at this point
void onPrepareDraws(Target* target) override {
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
GrQuad::Type quadType = GrQuad::Type::kAxisAligned;
GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned;
Domain domain = Domain::kNo;
ColorType colorType = ColorType::kNone;
int numProxies = 0;
int numTotalQuads = 0;
#ifdef SK_DEBUG
void validate() const override {
auto textureType = fProxies[0].fProxy->textureType();
const GrSwizzle& swizzle = fProxies[0].fProxy->textureSwizzle();
GrAAType aaType = this->aaType();
for (const auto& op : ChainRange<TextureOp>(this)) {
for (unsigned p = 0; p < op.fProxyCnt; ++p) {
auto* proxy = op.fProxies[p].fProxy;
SkASSERT(proxy);
SkASSERT(proxy->textureType() == textureType);
SkASSERT(proxy->textureSwizzle() == swizzle);
}
// Each individual op must be a single aaType. kCoverage and kNone ops can chain
// together but kMSAA ones do not.
if (aaType == GrAAType::kCoverage || aaType == GrAAType::kNone) {
SkASSERT(op.aaType() == GrAAType::kCoverage || op.aaType() == GrAAType::kNone);
} else {
SkASSERT(aaType == GrAAType::kMSAA && op.aaType() == GrAAType::kMSAA);
}
}
}
#endif
VertexSpec characterize(int* numProxies, int* numTotalQuads) const {
GrQuad::Type quadType = GrQuad::Type::kAxisAligned;
ColorType colorType = ColorType::kNone;
GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned;
Domain domain = Domain::kNo;
GrAAType overallAAType = this->aaType();
*numProxies = 0;
*numTotalQuads = 0;
for (const auto& op : ChainRange<TextureOp>(this)) {
if (op.fQuads.deviceQuadType() > quadType) {
quadType = op.fQuads.deviceQuadType();
@ -456,35 +482,28 @@ private:
domain = Domain::kYes;
}
colorType = SkTMax(colorType, static_cast<ColorType>(op.fColorType));
numProxies += op.fProxyCnt;
*numProxies += op.fProxyCnt;
for (unsigned p = 0; p < op.fProxyCnt; ++p) {
numTotalQuads += op.fProxies[p].fQuadCnt;
auto* proxy = op.fProxies[p].fProxy;
if (!proxy->isInstantiated()) {
return;
}
SkASSERT(proxy->textureType() == textureType);
SkASSERT(proxy->textureSwizzle() == swizzle);
*numTotalQuads += op.fProxies[p].fQuadCnt;
}
if (op.aaType() == GrAAType::kCoverage) {
SkASSERT(aaType == GrAAType::kCoverage || aaType == GrAAType::kNone);
aaType = GrAAType::kCoverage;
overallAAType = GrAAType::kCoverage;
}
}
VertexSpec vertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true, domain, aaType,
/* alpha as coverage */ true);
return VertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true, domain,
overallAAType, /* alpha as coverage */ true);
}
GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
this->filter());
GrGpu* gpu = target->resourceProvider()->priv().gpu();
uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
samplerState, fProxies[0].fProxy->backendFormat());
// onPrePrepareDraws may or may not have been called at this point
void onPrepareDraws(Target* target) override {
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
auto saturate = static_cast<GrTextureOp::Saturate>(fSaturate);
sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeTexturedProcessor(
vertexSpec, *target->caps().shaderCaps(), textureType, samplerState, swizzle,
extraSamplerKey, std::move(fTextureColorSpaceXform), saturate);
SkDEBUGCODE(this->validate();)
int numProxies, numTotalQuads;
const VertexSpec vertexSpec = this->characterize(&numProxies, &numTotalQuads);
// We'll use a dynamic state array for the GP textures when there are multiple ops.
// Otherwise, we use fixed dynamic state to specify the single op's proxy.
@ -498,7 +517,7 @@ private:
fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy;
}
size_t vertexSize = gp->vertexStride();
size_t vertexSize = vertexSpec.vertexSize();
GrMesh* meshes = target->allocMeshes(numProxies);
sk_sp<const GrBuffer> vbuffer;
@ -549,6 +568,29 @@ private:
}
SkASSERT(!numQuadVerticesLeft);
SkASSERT(!numAllocatedVertices);
sk_sp<GrGeometryProcessor> gp;
{
auto textureType = fProxies[0].fProxy->textureType();
const GrSwizzle& swizzle = fProxies[0].fProxy->textureSwizzle();
GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
this->filter());
auto saturate = static_cast<GrTextureOp::Saturate>(fSaturate);
GrGpu* gpu = target->resourceProvider()->priv().gpu();
uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
samplerState, fProxies[0].fProxy->backendFormat());
gp = GrQuadPerEdgeAA::MakeTexturedProcessor(
vertexSpec, *target->caps().shaderCaps(), textureType, samplerState, swizzle,
extraSamplerKey, std::move(fTextureColorSpaceXform), saturate);
SkASSERT(vertexSize == gp->vertexStride());
}
target->recordDraw(
std::move(gp), meshes, numProxies, fixedDynamicState, dynamicStateArrays);
}