From dbb833b45702f1c87c622eccbd1830719748379c Mon Sep 17 00:00:00 2001 From: Chris Dalton Date: Tue, 17 Mar 2020 12:15:46 -0600 Subject: [PATCH] Migrate GrFillRectOp and GrTextureOp to the new bind/draw API Renames GrQuadPerEdgeAA::ConfigureMesh to "IssueDraw", and modifies it to call draw*() directly on an opsRenderPass. Updates GrFillRectOp and GrTextureOp to bind their own pipelines, buffers, and textures, and then call IssueDraw. Removes DynamicStateArrays and FixedDynamicState from GrTextureOp, in favor of directly re-binding textures during onExecute. Bug: skia:9455 Change-Id: Ia4a7a467061bfd0e35d363295ef1821ed2ed4e9d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/277496 Reviewed-by: Michael Ludwig Reviewed-by: Robert Phillips Commit-Queue: Chris Dalton --- src/gpu/ops/GrFillRectOp.cpp | 35 ++-- src/gpu/ops/GrMeshDrawOp.h | 10 +- src/gpu/ops/GrQuadPerEdgeAA.cpp | 21 +-- src/gpu/ops/GrQuadPerEdgeAA.h | 12 +- src/gpu/ops/GrTextureOp.cpp | 321 ++++++++++++++------------------ 5 files changed, 176 insertions(+), 223 deletions(-) diff --git a/src/gpu/ops/GrFillRectOp.cpp b/src/gpu/ops/GrFillRectOp.cpp index e8bdef735a..d43cad53d7 100644 --- a/src/gpu/ops/GrFillRectOp.cpp +++ b/src/gpu/ops/GrFillRectOp.cpp @@ -291,14 +291,11 @@ private: // local coords. SkASSERT(!fHelper.isTrivial() || !fHelper.usesLocalCoords()); - sk_sp vertexBuffer; - int vertexOffsetInBuffer = 0; - const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad(); // Fill the allocated vertex data void* vdata = target->makeVertexSpace(vertexSpec.vertexSize(), totalNumVertices, - &vertexBuffer, &vertexOffsetInBuffer); + &fVertexBuffer, &fBaseVertex); if (!vdata) { SkDebugf("Could not allocate vertices\n"); return; @@ -312,24 +309,23 @@ private: this->tessellate(vertexSpec, (char*) vdata); } - sk_sp indexBuffer; if (vertexSpec.needsIndexBuffer()) { - indexBuffer = GrQuadPerEdgeAA::GetIndexBuffer(target, vertexSpec.indexBufferOption()); - if (!indexBuffer) { + fIndexBuffer = GrQuadPerEdgeAA::GetIndexBuffer(target, vertexSpec.indexBufferOption()); + if (!fIndexBuffer) { SkDebugf("Could not allocate indices\n"); return; } } - - // Configure the mesh for the vertex data - fMesh = target->allocMeshes(1); - GrQuadPerEdgeAA::ConfigureMesh(target->caps(), fMesh, vertexSpec, 0, fQuads.count(), - totalNumVertices, std::move(vertexBuffer), - std::move(indexBuffer), vertexOffsetInBuffer); } void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { - if (!fMesh) { + if (!fVertexBuffer) { + return; + } + + const VertexSpec vertexSpec = this->vertexSpec(); + + if (vertexSpec.needsIndexBuffer() && !fIndexBuffer) { return; } @@ -337,9 +333,13 @@ private: this->createProgramInfo(flushState); } + const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad(); + flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); + flushState->bindBuffers(fIndexBuffer.get(), nullptr, fVertexBuffer.get()); flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline()); - flushState->drawMesh(*fMesh); + GrQuadPerEdgeAA::IssueDraw(flushState->caps(), flushState->opsRenderPass(), vertexSpec, 0, + fQuads.count(), totalNumVertices, fBaseVertex); } CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*, @@ -451,10 +451,13 @@ private: GrQuadBuffer fQuads; char* fPrePreparedVertices = nullptr; - GrSimpleMesh* fMesh = nullptr; GrProgramInfo* fProgramInfo = nullptr; ColorType fColorType; + sk_sp fVertexBuffer; + sk_sp fIndexBuffer; + int fBaseVertex; + typedef GrMeshDrawOp INHERITED; }; diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h index 0a2fbee3ce..8dbab5590d 100644 --- a/src/gpu/ops/GrMeshDrawOp.h +++ b/src/gpu/ops/GrMeshDrawOp.h @@ -99,6 +99,11 @@ protected: : GrResourceProvider::MaxNumNonAAQuads()); } + virtual void onPrePrepareDraws(GrRecordingContext*, + const GrSurfaceProxyView* outputView, + GrAppliedClip*, + const GrXferProcessor::DstProxyView&); + private: virtual GrProgramInfo* programInfo() = 0; // This method is responsible for creating all the programInfos required @@ -117,11 +122,6 @@ private: } void onPrepare(GrOpFlushState* state) final; - virtual void onPrePrepareDraws(GrRecordingContext*, - const GrSurfaceProxyView* outputView, - GrAppliedClip*, - const GrXferProcessor::DstProxyView&); - virtual void onPrepareDraws(Target*) = 0; typedef GrDrawOp INHERITED; }; diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp index c0141aea99..e1c8b79612 100644 --- a/src/gpu/ops/GrQuadPerEdgeAA.cpp +++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp @@ -392,23 +392,17 @@ int QuadLimit(IndexBufferOption option) { SkUNREACHABLE; } -void ConfigureMesh(const GrCaps& caps, GrSimpleMesh* mesh, const VertexSpec& spec, - int runningQuadCount, int quadsInDraw, int maxVerts, - sk_sp vertexBuffer, - sk_sp indexBuffer, int absVertBufferOffset) { - SkASSERT(vertexBuffer); - +void IssueDraw(const GrCaps& caps, GrOpsRenderPass* renderPass, const VertexSpec& spec, + int runningQuadCount, int quadsInDraw, int maxVerts, int absVertBufferOffset) { if (spec.indexBufferOption() == IndexBufferOption::kTriStrips) { - SkASSERT(!indexBuffer); int offset = absVertBufferOffset + runningQuadCount * GrResourceProvider::NumVertsPerNonAAQuad(); - mesh->set(std::move(vertexBuffer), 4, offset); + renderPass->draw(4, offset); return; } SkASSERT(spec.indexBufferOption() == IndexBufferOption::kPictureFramed || spec.indexBufferOption() == IndexBufferOption::kIndexedRects); - SkASSERT(indexBuffer); int maxNumQuads, numIndicesPerQuad, numVertsPerQuad; @@ -432,8 +426,8 @@ void ConfigureMesh(const GrCaps& caps, GrSimpleMesh* mesh, const VertexSpec& spe // preferred. int offset = absVertBufferOffset + runningQuadCount * numVertsPerQuad; - mesh->setIndexedPatterned(std::move(indexBuffer), numIndicesPerQuad, quadsInDraw, - maxNumQuads, std::move(vertexBuffer), numVertsPerQuad, offset); + renderPass->drawIndexPattern(numIndicesPerQuad, quadsInDraw, maxNumQuads, numVertsPerQuad, + offset); } else { int baseIndex = runningQuadCount * numIndicesPerQuad; int numIndicesToDraw = quadsInDraw * numIndicesPerQuad; @@ -441,9 +435,8 @@ void ConfigureMesh(const GrCaps& caps, GrSimpleMesh* mesh, const VertexSpec& spe int minVertex = runningQuadCount * numVertsPerQuad; int maxVertex = (runningQuadCount + quadsInDraw) * numVertsPerQuad; - mesh->setIndexed(std::move(indexBuffer), numIndicesToDraw, - baseIndex, minVertex, maxVertex, GrPrimitiveRestart::kNo, - std::move(vertexBuffer), absVertBufferOffset); + renderPass->drawIndexed(numIndicesToDraw, baseIndex, minVertex, maxVertex, + absVertBufferOffset); } } diff --git a/src/gpu/ops/GrQuadPerEdgeAA.h b/src/gpu/ops/GrQuadPerEdgeAA.h index 174fa699e0..ec2f1c7fa6 100644 --- a/src/gpu/ops/GrQuadPerEdgeAA.h +++ b/src/gpu/ops/GrQuadPerEdgeAA.h @@ -182,9 +182,10 @@ namespace GrQuadPerEdgeAA { // What is the maximum number of quads allowed for the specified indexBuffer option? int QuadLimit(IndexBufferOption); - // 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. + // This method will issue the draw call on the provided GrOpsRenderPass, as specified by the + // indexing method in vertexSpec. It is up to the calling code to allocate, fill in, and bind a + // vertex buffer, and to acquire and bind the correct index buffer (if needed) with + // GrPrimitiveRestart::kNo. // // @param runningQuadCount the number of quads already stored in 'vertexBuffer' and // 'indexBuffer' e.g., different GrMeshes have already been placed in @@ -192,9 +193,8 @@ namespace GrQuadPerEdgeAA { // @param quadCount the number of quads that will be drawn by the provided 'mesh'. // A subsequent ConfigureMesh call would the use // 'runningQuadCount' + 'quadCount' for its new 'runningQuadCount'. - void ConfigureMesh(const GrCaps&, GrSimpleMesh*, const VertexSpec&, int runningQuadCount, - int quadCount, int maxVerts, sk_sp vertexBuffer, - sk_sp indexBuffer, int absVertBufferOffset); + void IssueDraw(const GrCaps&, GrOpsRenderPass*, const VertexSpec&, int runningQuadCount, + int quadCount, int maxVerts, int absVertBufferOffset); } // namespace GrQuadPerEdgeAA diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp index 1ab4dbdfe8..7cce0b02ef 100644 --- a/src/gpu/ops/GrTextureOp.cpp +++ b/src/gpu/ops/GrTextureOp.cpp @@ -237,6 +237,9 @@ public: for (unsigned p = 0; p < fMetadata.fProxyCount; ++p) { func(fViewCountPairs[p].fProxy.get(), GrMipMapped(mipped)); } + if (fDesc && fDesc->fProgramInfo) { + fDesc->fProgramInfo->visitFPProxies(func); + } } #ifdef SK_DEBUG @@ -376,69 +379,41 @@ private: }; static_assert(sizeof(Metadata) == 8); - // This descriptor is used in both onPrePrepareDraws and onPrepareDraws. + // This descriptor is used to store the draw info we decide on during on(Pre)PrepareDraws. We + // store the data in a separate struct in order to minimize the size of the TextureOp. + // Historically, increasing the TextureOp's size has caused surprising perf regressions, but we + // may want to re-evaluate whether this is still necessary. // - // In the onPrePrepareDraws case it is allocated in the creation-time opData - // arena. Both allocateCommon and allocatePrePrepareOnly are called and they also allocate - // their memory in the creation-time opData arena. + // In the onPrePrepareDraws case it is allocated in the creation-time opData arena, and + // allocatePrePreparedVertices is also called. // - // In the onPrepareDraws case this descriptor is created on the stack and only - // allocateCommon is called. In this case the common memory fields are allocated - // in the flush-time arena (i.e., as part of the flushState). - struct PrePreparedDesc { - VertexSpec fVertexSpec; - int fNumProxies = 0; - int fNumTotalQuads = 0; - GrPipeline::DynamicStateArrays* fDynamicStateArrays = nullptr; - GrPipeline::FixedDynamicState* fFixedDynamicState = nullptr; + // In the onPrepareDraws case this descriptor is allocated in the flush-time arena (i.e., as + // part of the flushState). + struct Desc { + VertexSpec fVertexSpec; + int fNumProxies = 0; + int fNumTotalQuads = 0; - // This member variable is only used by 'onPrePrepareDraws'. The prior five are also - // used by 'onPrepareDraws' - char* fVertices = nullptr; + // This member variable is only used by 'onPrePrepareDraws'. + char* fPrePreparedVertices = nullptr; + + GrProgramInfo* fProgramInfo = nullptr; + + sk_sp fIndexBuffer; + sk_sp fVertexBuffer; + int fBaseVertex; // How big should 'fVertices' be to hold all the vertex data? size_t totalSizeInBytes() const { - return fNumTotalQuads * fVertexSpec.verticesPerQuad() * fVertexSpec.vertexSize(); + return this->totalNumVertices() * fVertexSpec.vertexSize(); } int totalNumVertices() const { return fNumTotalQuads * fVertexSpec.verticesPerQuad(); } - // Helper to fill in the fFixedDynamicState and fDynamicStateArrays. If there is more - // than one mesh/proxy they are stored in fDynamicStateArrays but if there is only one - // it is stored in fFixedDynamicState. - void setMeshProxy(int index, GrSurfaceProxy* proxy) { - SkASSERT(index < fNumProxies); - - if (fDynamicStateArrays) { - SkASSERT(fDynamicStateArrays->fPrimitiveProcessorTextures); - SkASSERT(fNumProxies > 1); - - fDynamicStateArrays->fPrimitiveProcessorTextures[index] = proxy; - } else { - SkASSERT(fFixedDynamicState); - SkASSERT(fNumProxies == 1); - - fFixedDynamicState->fPrimitiveProcessorTextures[index] = proxy; - } - } - - // Allocate the fields required in both onPrePrepareDraws and onPrepareDraws - void allocateCommon(SkArenaAlloc* arena, const GrAppliedClip* clip) { - // 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. - if (fNumProxies > 1) { - fDynamicStateArrays = Target::AllocDynamicStateArrays(arena, fNumProxies, 1, false); - fFixedDynamicState = Target::MakeFixedDynamicState(arena, clip, 0); - } else { - fFixedDynamicState = Target::MakeFixedDynamicState(arena, clip, 1); - } - } - - // Allocate the fields only needed by onPrePrepareDraws - void allocatePrePrepareOnly(SkArenaAlloc* arena) { - fVertices = arena->makeArrayDefault(this->totalSizeInBytes()); + void allocatePrePreparedVertices(SkArenaAlloc* arena) { + fPrePreparedVertices = arena->makeArrayDefault(this->totalSizeInBytes()); } }; @@ -455,7 +430,7 @@ private: : INHERITED(ClassID()) , fQuads(1, true /* includes locals */) , fTextureColorSpaceXform(std::move(textureColorSpaceXform)) - , fPrePreparedDesc(nullptr) + , fDesc(nullptr) , fMetadata(proxyView.swizzle(), filter, Domain(!!domainRect), saturate) { // Clean up disparities between the overall aa type and edge configuration and apply @@ -504,7 +479,7 @@ private: : INHERITED(ClassID()) , fQuads(cnt, true /* includes locals */) , fTextureColorSpaceXform(std::move(textureColorSpaceXform)) - , fPrePreparedDesc(nullptr) + , fDesc(nullptr) , fMetadata(set[0].fProxyView.swizzle(), GrSamplerState::Filter::kNearest, Domain::kNo, saturate) { // Update counts to reflect the batch op @@ -643,17 +618,41 @@ private: } GrProgramInfo* programInfo() override { - // This Op implements its own onPrePrepareDraws so this entry point should never be called. - SkASSERT(0); - return nullptr; + // Although this Op implements its own onPrePrepareDraws it calls GrMeshDrawOps' version so + // this entry point will be called. + return (fDesc) ? fDesc->fProgramInfo : nullptr; } - void onCreateProgramInfo(const GrCaps*, - SkArenaAlloc*, + void onCreateProgramInfo(const GrCaps* caps, + SkArenaAlloc* arena, const GrSurfaceProxyView* outputView, - GrAppliedClip&&, - const GrXferProcessor::DstProxyView&) override { - // TODO [PI]: implement + GrAppliedClip&& appliedClip, + const GrXferProcessor::DstProxyView& dstProxyView) override { + SkASSERT(fDesc); + + GrGeometryProcessor* gp; + + { + const GrBackendFormat& backendFormat = + fViewCountPairs[0].fProxy->backendFormat(); + + GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp, + fMetadata.filter()); + + gp = GrQuadPerEdgeAA::MakeTexturedProcessor( + arena, fDesc->fVertexSpec, *caps->shaderCaps(), backendFormat, samplerState, + fMetadata.fSwizzle, std::move(fTextureColorSpaceXform), fMetadata.saturate()); + + SkASSERT(fDesc->fVertexSpec.vertexSize() == gp->vertexStride()); + } + + auto pipelineFlags = (GrAAType::kMSAA == fMetadata.aaType()) ? + GrPipeline::InputFlags::kHWAntialias : GrPipeline::InputFlags::kNone; + + fDesc->fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo( + caps, arena, outputView, std::move(appliedClip), dstProxyView, gp, + GrProcessorSet::MakeEmptySet(), fDesc->fVertexSpec.primitiveType(), + pipelineFlags); } void onPrePrepareDraws(GrRecordingContext* context, @@ -663,71 +662,52 @@ private: TRACE_EVENT0("skia.gpu", TRACE_FUNC); SkDEBUGCODE(this->validate();) - SkASSERT(!fPrePreparedDesc); + SkASSERT(!fDesc); SkArenaAlloc* arena = context->priv().recordTimeAllocator(); - fPrePreparedDesc = arena->make(); + fDesc = arena->make(); + this->characterize(fDesc); + fDesc->allocatePrePreparedVertices(arena); + FillInVertices(*context->priv().caps(), this, fDesc, fDesc->fPrePreparedVertices); - this->characterize(fPrePreparedDesc); - - fPrePreparedDesc->allocateCommon(arena, clip); - - fPrePreparedDesc->allocatePrePrepareOnly(arena); - - // At this juncture we only fill in the vertex data and state arrays. Filling in of - // the meshes is left until onPrepareDraws. - SkAssertResult(FillInData(*context->priv().caps(), this, fPrePreparedDesc, - fPrePreparedDesc->fVertices, nullptr, 0, nullptr, nullptr)); + // This will call onCreateProgramInfo and register the created program with the DDL. + this->INHERITED::onPrePrepareDraws(context, outputView, clip, dstProxyView); } - static bool FillInData(const GrCaps& caps, TextureOp* texOp, PrePreparedDesc* desc, - char* pVertexData, GrSimpleMesh* meshes, int absBufferOffset, - sk_sp vertexBuffer, - sk_sp indexBuffer) { + static void FillInVertices(const GrCaps& caps, TextureOp* texOp, Desc* desc, char* vertexData) { + SkASSERT(vertexData); + int totQuadsSeen = 0; SkDEBUGCODE(int totVerticesSeen = 0;) SkDEBUGCODE(const size_t vertexSize = desc->fVertexSpec.vertexSize()); - GrQuadPerEdgeAA::Tessellator tessellator(desc->fVertexSpec, pVertexData); - int meshIndex = 0; + GrQuadPerEdgeAA::Tessellator tessellator(desc->fVertexSpec, vertexData); for (const auto& op : ChainRange(texOp)) { auto iter = op.fQuads.iterator(); for (unsigned p = 0; p < op.fMetadata.fProxyCount; ++p) { const int quadCnt = op.fViewCountPairs[p].fQuadCnt; SkDEBUGCODE(int meshVertexCnt = quadCnt * desc->fVertexSpec.verticesPerQuad()); - SkASSERT(meshIndex < desc->fNumProxies); - if (pVertexData) { - // Can just use top-left for origin here since we only need the dimensions to - // determine the texel size for insetting. - NormalizationParams params = proxy_normalization_params( - op.fViewCountPairs[p].fProxy.get(), kTopLeft_GrSurfaceOrigin); + // Can just use top-left for origin here since we only need the dimensions to + // determine the texel size for insetting. + NormalizationParams params = proxy_normalization_params( + op.fViewCountPairs[p].fProxy.get(), kTopLeft_GrSurfaceOrigin); - bool inset = texOp->fMetadata.filter() != GrSamplerState::Filter::kNearest; + bool inset = texOp->fMetadata.filter() != GrSamplerState::Filter::kNearest; - for (int i = 0; i < quadCnt && iter.next(); ++i) { - SkASSERT(iter.isLocalValid()); - const ColorDomainAndAA& info = iter.metadata(); + for (int i = 0; i < quadCnt && iter.next(); ++i) { + SkASSERT(iter.isLocalValid()); + const ColorDomainAndAA& info = iter.metadata(); - tessellator.append(iter.deviceQuad(), iter.localQuad(), info.fColor, - inset ? inset_domain_for_bilerp(params, info.fDomainRect) - : info.fDomainRect, - info.aaFlags()); - } - desc->setMeshProxy(meshIndex, op.fViewCountPairs[p].fProxy.get()); - - SkASSERT((totVerticesSeen + meshVertexCnt) * vertexSize - == (size_t)(tessellator.vertices() - pVertexData)); + tessellator.append(iter.deviceQuad(), iter.localQuad(), info.fColor, + inset ? inset_domain_for_bilerp(params, info.fDomainRect) + : info.fDomainRect, + info.aaFlags()); } - if (meshes) { - GrQuadPerEdgeAA::ConfigureMesh(caps, &(meshes[meshIndex]), desc->fVertexSpec, - totQuadsSeen, quadCnt, desc->totalNumVertices(), - vertexBuffer, indexBuffer, absBufferOffset); - } - - ++meshIndex; + SkASSERT((totVerticesSeen + meshVertexCnt) * vertexSize + == (size_t)(tessellator.vertices() - vertexData)); totQuadsSeen += quadCnt; SkDEBUGCODE(totVerticesSeen += meshVertexCnt); @@ -736,15 +716,12 @@ private: // If quad counts per proxy were calculated correctly, the entire iterator // should have been consumed. - SkASSERT(!pVertexData || !iter.next()); + SkASSERT(!iter.next()); } - SkASSERT(!pVertexData || - (desc->totalSizeInBytes() == (size_t)(tessellator.vertices() - pVertexData))); - SkASSERT(meshIndex == desc->fNumProxies); + SkASSERT(desc->totalSizeInBytes() == (size_t)(tessellator.vertices() - vertexData)); SkASSERT(totQuadsSeen == desc->fNumTotalQuads); SkASSERT(totVerticesSeen == desc->totalNumVertices()); - return true; } #ifdef SK_DEBUG @@ -782,7 +759,7 @@ private: int numQuads() const final { return this->totNumQuads(); } #endif - void characterize(PrePreparedDesc* desc) const { + void characterize(Desc* desc) const { GrQuad::Type quadType = GrQuad::Type::kAxisAligned; ColorType colorType = ColorType::kNone; GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned; @@ -862,93 +839,76 @@ private: SkDEBUGCODE(this->validate();) - PrePreparedDesc desc; + SkASSERT(!fDesc || fDesc->fPrePreparedVertices); - if (fPrePreparedDesc) { - desc = *fPrePreparedDesc; - } else { + if (!fDesc) { SkArenaAlloc* arena = target->allocator(); - - this->characterize(&desc); - desc.allocateCommon(arena, target->appliedClip()); - - SkASSERT(!desc.fVertices); + fDesc = arena->make(); + this->characterize(fDesc); + SkASSERT(!fDesc->fPrePreparedVertices); } - size_t vertexSize = desc.fVertexSpec.vertexSize(); + size_t vertexSize = fDesc->fVertexSpec.vertexSize(); - sk_sp vbuffer; - int vertexOffsetInBuffer = 0; - - void* vdata = target->makeVertexSpace(vertexSize, desc.totalNumVertices(), - &vbuffer, &vertexOffsetInBuffer); + void* vdata = target->makeVertexSpace(vertexSize, fDesc->totalNumVertices(), + &fDesc->fVertexBuffer, &fDesc->fBaseVertex); if (!vdata) { SkDebugf("Could not allocate vertices\n"); return; } - sk_sp indexBuffer; - if (desc.fVertexSpec.needsIndexBuffer()) { - indexBuffer = GrQuadPerEdgeAA::GetIndexBuffer(target, - desc.fVertexSpec.indexBufferOption()); - if (!indexBuffer) { + if (fDesc->fVertexSpec.needsIndexBuffer()) { + fDesc->fIndexBuffer = GrQuadPerEdgeAA::GetIndexBuffer( + target, fDesc->fVertexSpec.indexBufferOption()); + if (!fDesc->fIndexBuffer) { SkDebugf("Could not allocate indices\n"); return; } } - // Note: this allocation is always in the flush-time arena (i.e., the flushState) - GrSimpleMesh* meshes = target->allocMeshes(desc.fNumProxies); - - bool result; - if (fPrePreparedDesc) { - memcpy(vdata, desc.fVertices, desc.totalSizeInBytes()); - // The above memcpy filled in the vertex data - just call FillInData to fill in the - // mesh data - result = FillInData(target->caps(), this, &desc, nullptr, meshes, vertexOffsetInBuffer, - std::move(vbuffer), std::move(indexBuffer)); + if (fDesc->fPrePreparedVertices) { + memcpy(vdata, fDesc->fPrePreparedVertices, fDesc->totalSizeInBytes()); } else { - // Fills in both vertex data and mesh data - result = FillInData(target->caps(), this, &desc, (char*) vdata, meshes, - vertexOffsetInBuffer, std::move(vbuffer), std::move(indexBuffer)); + FillInVertices(target->caps(), this, fDesc, (char*) vdata); } - - if (!result) { - return; - } - - GrGeometryProcessor* gp; - - { - const GrBackendFormat& backendFormat = - fViewCountPairs[0].fProxy->backendFormat(); - - GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp, - fMetadata.filter()); - - gp = GrQuadPerEdgeAA::MakeTexturedProcessor(target->allocator(), - desc.fVertexSpec, *target->caps().shaderCaps(), backendFormat, - samplerState, fMetadata.fSwizzle, std::move(fTextureColorSpaceXform), - fMetadata.saturate()); - - SkASSERT(vertexSize == gp->vertexStride()); - } - - target->recordDraw(gp, meshes, desc.fNumProxies, - desc.fFixedDynamicState, desc.fDynamicStateArrays, - desc.fVertexSpec.primitiveType()); } void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { - auto pipelineFlags = (GrAAType::kMSAA == fMetadata.aaType()) - ? GrPipeline::InputFlags::kHWAntialias - : GrPipeline::InputFlags::kNone; + if (!fDesc->fVertexBuffer) { + return; + } - auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, - GrProcessorSet::MakeEmptySet(), - pipelineFlags); + if (fDesc->fVertexSpec.needsIndexBuffer() && !fDesc->fIndexBuffer) { + return; + } - flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline); + if (!fDesc->fProgramInfo) { + this->createProgramInfo(flushState); + SkASSERT(fDesc->fProgramInfo); + } + + flushState->bindPipelineAndScissorClip(*fDesc->fProgramInfo, chainBounds); + flushState->bindBuffers(fDesc->fIndexBuffer.get(), nullptr, fDesc->fVertexBuffer.get()); + + int totQuadsSeen = 0; + SkDEBUGCODE(int numDraws = 0;) + for (const auto& op : ChainRange(this)) { + for (unsigned p = 0; p < op.fMetadata.fProxyCount; ++p) { + const int quadCnt = op.fViewCountPairs[p].fQuadCnt; + SkASSERT(numDraws < fDesc->fNumProxies); + flushState->bindTextures(fDesc->fProgramInfo->primProc(), + *op.fViewCountPairs[p].fProxy, + fDesc->fProgramInfo->pipeline()); + GrQuadPerEdgeAA::IssueDraw(flushState->caps(), flushState->opsRenderPass(), + fDesc->fVertexSpec, totQuadsSeen, quadCnt, + fDesc->totalNumVertices(), fDesc->fBaseVertex); + totQuadsSeen += quadCnt; + SkDEBUGCODE(++numDraws;) + } + } + + SkASSERT(totQuadsSeen == fDesc->fNumTotalQuads); + SkASSERT(numDraws == fDesc->fNumProxies); } CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*, @@ -956,7 +916,7 @@ private: TRACE_EVENT0("skia.gpu", TRACE_FUNC); const auto* that = t->cast(); - if (fPrePreparedDesc || that->fPrePreparedDesc) { + if (fDesc || that->fDesc) { // This should never happen (since only DDL recorded ops should be prePrepared) // but, in any case, we should never combine ops that that been prePrepared return CombineResult::kCannotCombine; @@ -1023,13 +983,10 @@ private: GrQuadBuffer fQuads; sk_sp fTextureColorSpaceXform; - // 'fPrePreparedDesc' is only filled in when this op has been prePrepared. In that case, - // it - and the matching dynamic and fixed state - have been allocated in the opPOD arena - // not in the FlushState arena. - PrePreparedDesc* fPrePreparedDesc; - // All configurable state of TextureOp is packed into one field to minimize the op's size. + // Most state of TextureOp is packed into these two field to minimize the op's size. // Historically, increasing the size of TextureOp has caused surprising perf regressions, so // consider/measure changes with care. + Desc* fDesc; Metadata fMetadata; // This field must go last. When allocating this op, we will allocate extra space to hold