Add GrOpsRenderPass::bindPipeline()
Clients now must call bindPipeline() before drawing.
Also renames GrOpsRenderPass::draw() to drawMeshes(), in order to
ensure every call site gets updated. drawMeshes() will soon be
replaced by individual calls for each draw type (indexed, instanced,
indexed-patterned, indirect, etc.).
Change-Id: I93ef579ded7d0048c5aa1bf1d7c0eb7bc1cd27b2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/270424
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/gm/clockwise.cpp b/gm/clockwise.cpp
index 07892ac..25ff881 100644
--- a/gm/clockwise.cpp
+++ b/gm/clockwise.cpp
@@ -213,8 +213,8 @@
mesh.setNonIndexedNonInstanced(4);
mesh.setVertexData(std::move(fVertexBuffer));
- flushState->opsRenderPass()->draw(*fProgramInfo, &mesh, 1,
- SkRect::MakeXYWH(0, fY, 100, 100));
+ flushState->opsRenderPass()->bindPipeline(*fProgramInfo, SkRect::MakeXYWH(0, fY, 100, 100));
+ flushState->opsRenderPass()->drawMeshes(*fProgramInfo, &mesh, 1);
}
sk_sp<GrBuffer> fVertexBuffer;
diff --git a/gm/fwidth_squircle.cpp b/gm/fwidth_squircle.cpp
index 76aa830..97a88ae 100644
--- a/gm/fwidth_squircle.cpp
+++ b/gm/fwidth_squircle.cpp
@@ -221,8 +221,8 @@
mesh.setNonIndexedNonInstanced(4);
mesh.setVertexData(std::move(fVertexBuffer));
- flushState->opsRenderPass()->draw(*fProgramInfo, &mesh, 1,
- SkRect::MakeIWH(kWidth, kHeight));
+ flushState->opsRenderPass()->bindPipeline(*fProgramInfo, SkRect::MakeIWH(kWidth, kHeight));
+ flushState->opsRenderPass()->drawMeshes(*fProgramInfo, &mesh, 1);
}
diff --git a/gm/samplelocations.cpp b/gm/samplelocations.cpp
index e131852..2bba40c 100644
--- a/gm/samplelocations.cpp
+++ b/gm/samplelocations.cpp
@@ -285,7 +285,8 @@
GrMesh mesh;
mesh.setInstanced(nullptr, 200*200, 0, 4);
- flushState->opsRenderPass()->draw(*fProgramInfo, &mesh, 1, SkRect::MakeIWH(200, 200));
+ flushState->opsRenderPass()->bindPipeline(*fProgramInfo, SkRect::MakeIWH(200, 200));
+ flushState->opsRenderPass()->drawMeshes(*fProgramInfo, &mesh, 1);
}
const GradType fGradType;
diff --git a/gm/tessellation.cpp b/gm/tessellation.cpp
index 0eec638..e390b0d 100644
--- a/gm/tessellation.cpp
+++ b/gm/tessellation.cpp
@@ -337,7 +337,8 @@
&pipeline, shader.get(), &fixedDynamicState, nullptr, 0,
GrPrimitiveType::kPatches, tessellationPatchVertexCount);
- state->opsRenderPass()->draw(programInfo, &mesh, 1, SkRect::MakeIWH(kWidth, kHeight));
+ state->opsRenderPass()->bindPipeline(programInfo, SkRect::MakeIWH(kWidth, kHeight));
+ state->opsRenderPass()->drawMeshes(programInfo, &mesh, 1);
}
const SkMatrix fViewMatrix;
diff --git a/src/gpu/GrOpFlushState.cpp b/src/gpu/GrOpFlushState.cpp
index cb8b13b..5364bf1 100644
--- a/src/gpu/GrOpFlushState.cpp
+++ b/src/gpu/GrOpFlushState.cpp
@@ -54,8 +54,8 @@
fCurrDraw->fMeshCnt,
fCurrDraw->fPrimitiveType);
- this->opsRenderPass()->draw(programInfo, fCurrDraw->fMeshes,
- fCurrDraw->fMeshCnt, chainBounds);
+ this->opsRenderPass()->bindPipeline(programInfo, chainBounds);
+ this->opsRenderPass()->drawMeshes(programInfo, fCurrDraw->fMeshes, fCurrDraw->fMeshCnt);
fTokenTracker->flushToken();
++fCurrDraw;
}
diff --git a/src/gpu/GrOpsRenderPass.cpp b/src/gpu/GrOpsRenderPass.cpp
index 8626ada..dd511f7 100644
--- a/src/gpu/GrOpsRenderPass.cpp
+++ b/src/gpu/GrOpsRenderPass.cpp
@@ -26,41 +26,53 @@
// be redirected to draws instead
SkASSERT(!this->gpu()->caps()->performColorClearsAsDraws());
SkASSERT(!clip.scissorEnabled() || !this->gpu()->caps()->performPartialClearsAsDraws());
+ fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured;
this->onClear(clip, color);
}
void GrOpsRenderPass::clearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
// As above, make sure the stencil clear wasn't supposed to be a draw rect with stencil settings
SkASSERT(!this->gpu()->caps()->performStencilClearsAsDraws());
+ fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured;
this->onClearStencilClip(clip, insideStencilMask);
}
-bool GrOpsRenderPass::draw(const GrProgramInfo& programInfo,
- const GrMesh meshes[], int meshCount, const SkRect& bounds) {
- if (!meshCount) {
- return true;
- }
+void GrOpsRenderPass::executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable) {
+ fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured;
+ this->onExecuteDrawable(std::move(drawable));
+}
+void GrOpsRenderPass::bindPipeline(const GrProgramInfo& programInfo, const SkRect& drawBounds) {
#ifdef SK_DEBUG
- SkASSERT(GrPrimitiveType::kPatches != programInfo.primitiveType() ||
- this->gpu()->caps()->shaderCaps()->tessellationSupport());
- SkASSERT(!programInfo.primProc().hasInstanceAttributes() ||
- this->gpu()->caps()->instanceAttribSupport());
- SkASSERT(!programInfo.pipeline().usesConservativeRaster() ||
- this->gpu()->caps()->conservativeRasterSupport());
- SkASSERT(!programInfo.pipeline().isWireframe() ||
- this->gpu()->caps()->wireframeSupport());
-
- programInfo.compatibleWithMeshes(meshes, meshCount, *this->gpu()->caps());
+ if (programInfo.primProc().hasInstanceAttributes()) {
+ SkASSERT(this->gpu()->caps()->instanceAttribSupport());
+ }
+ if (programInfo.pipeline().usesConservativeRaster()) {
+ SkASSERT(this->gpu()->caps()->conservativeRasterSupport());
+ // Conservative raster, by default, only supports triangles. Implementations can
+ // optionally indicate that they also support points and lines, but we don't currently
+ // query or track that info.
+ SkASSERT(GrIsPrimTypeTris(programInfo.primitiveType()));
+ }
+ if (programInfo.pipeline().isWireframe()) {
+ SkASSERT(this->gpu()->caps()->wireframeSupport());
+ }
+ if (GrPrimitiveType::kPatches == programInfo.primitiveType()) {
+ SkASSERT(this->gpu()->caps()->shaderCaps()->tessellationSupport());
+ }
programInfo.checkAllInstantiated();
programInfo.checkMSAAAndMIPSAreResolved();
#endif
if (programInfo.primProc().numVertexAttributes() > this->gpu()->caps()->maxVertexAttributes()) {
- this->gpu()->stats()->incNumFailedDraws();
- return false;
+ fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind;
+ return;
}
- this->onDraw(programInfo, meshes, meshCount, bounds);
+
+ if (!this->onBindPipeline(programInfo, drawBounds)) {
+ fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind;
+ return;
+ }
#ifdef SK_DEBUG
GrProcessor::CustomFeatures processorFeatures = programInfo.requestedFeatures();
@@ -70,5 +82,34 @@
== fRenderTarget->renderTargetPriv().getSamplePatternKey());
}
#endif
- return true;
+
+ fDrawPipelineStatus = DrawPipelineStatus::kOk;
+}
+
+void GrOpsRenderPass::drawMeshes(const GrProgramInfo& programInfo, const GrMesh meshes[],
+ int meshCount) {
+ if (DrawPipelineStatus::kOk != fDrawPipelineStatus) {
+ SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus);
+ this->gpu()->stats()->incNumFailedDraws();
+ return;
+ }
+
+#ifdef SK_DEBUG
+ if (int numDynamicStateArrays = programInfo.numDynamicStateArrays()) {
+ SkASSERT(meshCount == numDynamicStateArrays);
+ }
+ for (int i = 0; i < meshCount; ++i) {
+ SkASSERT(programInfo.primProc().hasVertexAttributes() ==
+ SkToBool(meshes[i].vertexBuffer()));
+ SkASSERT(programInfo.primProc().hasInstanceAttributes() ==
+ SkToBool(meshes[i].instanceBuffer()));
+ if (GrPrimitiveRestart::kYes == meshes[i].primitiveRestart()) {
+ SkASSERT(this->gpu()->caps()->usePrimitiveRestart());
+ }
+ }
+#endif
+
+ if (meshCount) {
+ this->onDrawMeshes(programInfo, meshes, meshCount);
+ }
}
diff --git a/src/gpu/GrOpsRenderPass.h b/src/gpu/GrOpsRenderPass.h
index 4b2abc1..0652b26 100644
--- a/src/gpu/GrOpsRenderPass.h
+++ b/src/gpu/GrOpsRenderPass.h
@@ -50,11 +50,16 @@
// Signals the end of recording to the GrOpsRenderPass and that it can now be submitted.
virtual void end() = 0;
- // We pass in an array of meshCount GrMesh to the draw. The backend should loop over each
- // GrMesh object and emit a draw for it. Each draw will use the same GrPipeline and
- // GrPrimitiveProcessor. This may fail if the draw would exceed any resource limits (e.g.
- // number of vertex attributes is too large).
- bool draw(const GrProgramInfo&, const GrMesh[], int meshCount, const SkRect& bounds);
+ // Updates the internal pipeline state for drawing with the provided GrProgramInfo.
+ // Returns false if the state could not be set.
+ void bindPipeline(const GrProgramInfo&, const SkRect& drawBounds);
+
+ // Draws the given array of meshes using the current pipeline state. The client must call
+ // bindPipeline() before using this method.
+ //
+ // NOTE: This method will soon be replaced by individual calls for each draw type (indexed,
+ // instanced, indexed-patterned, indirect, etc.).
+ void drawMeshes(const GrProgramInfo&, const GrMesh[], int meshCount);
// Performs an upload of vertex data in the middle of a set of a set of draws
virtual void inlineUpload(GrOpFlushState*, GrDeferredTextureUploadFn&) = 0;
@@ -69,7 +74,7 @@
/**
* Executes the SkDrawable object for the underlying backend.
*/
- virtual void executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) {}
+ void executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>);
protected:
GrOpsRenderPass() : fOrigin(kTopLeft_GrSurfaceOrigin), fRenderTarget(nullptr) {}
@@ -92,14 +97,20 @@
private:
virtual GrGpu* gpu() = 0;
- // overridden by backend-specific derived class to perform the draw call.
- virtual void onDraw(const GrProgramInfo&, const GrMesh[], int meshCount,
- const SkRect& bounds) = 0;
-
- // overridden by backend-specific derived class to perform the clear.
+ // overridden by backend-specific derived class to perform the rendering command.
+ virtual bool onBindPipeline(const GrProgramInfo&, const SkRect& drawBounds) = 0;
+ virtual void onDrawMeshes(const GrProgramInfo&, const GrMesh[], int meshCount) = 0;
virtual void onClear(const GrFixedClip&, const SkPMColor4f&) = 0;
-
virtual void onClearStencilClip(const GrFixedClip&, bool insideStencilMask) = 0;
+ virtual void onExecuteDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) {}
+
+ enum class DrawPipelineStatus {
+ kOk = 0,
+ kNotConfigured,
+ kFailedToBind
+ };
+
+ DrawPipelineStatus fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured;
typedef GrOpsRenderPass INHERITED;
};
diff --git a/src/gpu/GrProgramInfo.cpp b/src/gpu/GrProgramInfo.cpp
index 152ee19..636efaa 100644
--- a/src/gpu/GrProgramInfo.cpp
+++ b/src/gpu/GrProgramInfo.cpp
@@ -118,22 +118,4 @@
}
}
-void GrProgramInfo::compatibleWithMeshes(const GrMesh meshes[], int meshCount,
- const GrCaps& caps) const {
- SkASSERT(!fNumDynamicStateArrays || meshCount == fNumDynamicStateArrays);
-
- for (int i = 0; i < meshCount; ++i) {
- SkASSERT(fPrimProc->hasVertexAttributes() == SkToBool(meshes[i].vertexBuffer()));
- SkASSERT(fPrimProc->hasInstanceAttributes() == SkToBool(meshes[i].instanceBuffer()));
- if (fPipeline->usesConservativeRaster()) {
- // Conservative raster, by default, only supports triangles. Implementations can
- // optionally indicate that they also support points and lines, but we don't currently
- // query or track that info.
- SkASSERT(GrIsPrimTypeTris(fPrimitiveType));
- }
- SkASSERT(GrPrimitiveRestart::kNo == meshes[i].primitiveRestart() ||
- caps.usePrimitiveRestart());
- }
-}
-
#endif
diff --git a/src/gpu/GrProgramInfo.h b/src/gpu/GrProgramInfo.h
index 2bd31c9..7f4a1f0 100644
--- a/src/gpu/GrProgramInfo.h
+++ b/src/gpu/GrProgramInfo.h
@@ -62,6 +62,7 @@
const GrPipeline& pipeline() const { return *fPipeline; }
const GrPrimitiveProcessor& primProc() const { return *fPrimProc; }
const GrPipeline::FixedDynamicState* fixedDynamicState() const { return fFixedDynamicState; }
+ int numDynamicStateArrays() const { return fNumDynamicStateArrays; }
bool hasDynamicScissors() const {
return fPipeline->isScissorEnabled() &&
@@ -126,7 +127,6 @@
void validate(bool flushTime) const;
void checkAllInstantiated() const;
void checkMSAAAndMIPSAreResolved() const;
- void compatibleWithMeshes(const GrMesh meshes[], int meshCount, const GrCaps&) const;
bool isNVPR() const {
return fPrimProc->isPathRendering() && !fPrimProc->willUseGeoShader() &&
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
index bbe0bdd..14d9887 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
@@ -214,5 +214,6 @@
&dynamicStateArrays, 0, primitiveType);
- renderPass->draw(programInfo, meshes, meshCount, drawBounds);
+ renderPass->bindPipeline(programInfo, drawBounds);
+ renderPass->drawMeshes(programInfo, meshes, meshCount);
}
diff --git a/src/gpu/ccpr/GrCCPathProcessor.cpp b/src/gpu/ccpr/GrCCPathProcessor.cpp
index ed9d4eb..8499726 100644
--- a/src/gpu/ccpr/GrCCPathProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPathProcessor.cpp
@@ -150,7 +150,8 @@
fixedDynamicState,
nullptr, 0, primitiveType);
- flushState->opsRenderPass()->draw(programInfo, &mesh, 1, bounds);
+ flushState->opsRenderPass()->bindPipeline(programInfo, bounds);
+ flushState->opsRenderPass()->drawMeshes(programInfo, &mesh, 1);
}
void GrCCPathProcessor::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
diff --git a/src/gpu/ccpr/GrCCStroker.cpp b/src/gpu/ccpr/GrCCStroker.cpp
index 171bc55..8d092d6 100644
--- a/src/gpu/ccpr/GrCCStroker.cpp
+++ b/src/gpu/ccpr/GrCCStroker.cpp
@@ -788,9 +788,9 @@
nullptr,
&dynamicStateArrays, 0, GrPrimitiveType::kTriangleStrip);
- flushState->opsRenderPass()->draw(programInfo,
- fMeshesBuffer.begin(), fMeshesBuffer.count(),
- SkRect::Make(drawBounds));
+ flushState->opsRenderPass()->bindPipeline(programInfo, SkRect::Make(drawBounds));
+ flushState->opsRenderPass()->drawMeshes(programInfo, fMeshesBuffer.begin(),
+ fMeshesBuffer.count());
// Don't call reset(), as that also resets the reserve count.
fMeshesBuffer.pop_back_n(fMeshesBuffer.count());
fScissorsBuffer.pop_back_n(fScissorsBuffer.count());
diff --git a/src/gpu/ccpr/GrStencilAtlasOp.cpp b/src/gpu/ccpr/GrStencilAtlasOp.cpp
index 0f5ba47..e56d722 100644
--- a/src/gpu/ccpr/GrStencilAtlasOp.cpp
+++ b/src/gpu/ccpr/GrStencilAtlasOp.cpp
@@ -163,5 +163,6 @@
&scissorRectState,
nullptr, 0, GrPrimitiveType::kTriangleStrip);
- flushState->opsRenderPass()->draw(programInfo, &mesh, 1, SkRect::Make(drawBoundsRect));
+ flushState->opsRenderPass()->bindPipeline(programInfo, SkRect::Make(drawBoundsRect));
+ flushState->opsRenderPass()->drawMeshes(programInfo, &mesh, 1);
}
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 41f448b..3827733 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1809,6 +1809,11 @@
}
bool GrGLGpu::flushGLState(GrRenderTarget* renderTarget, const GrProgramInfo& programInfo) {
+ this->handleDirtyContext();
+
+ if (GrPrimitiveType::kPatches == programInfo.primitiveType()) {
+ this->flushPatchVertexCount(programInfo.tessellationPatchVertexCount());
+ }
sk_sp<GrGLProgram> program(fProgramCache->findOrCreateProgram(renderTarget, programInfo));
if (!program) {
@@ -2350,22 +2355,10 @@
#endif
#endif
-void GrGLGpu::draw(GrRenderTarget* renderTarget,
- const GrProgramInfo& programInfo,
- const GrMesh meshes[],
- int meshCount) {
- this->handleDirtyContext();
-
+void GrGLGpu::drawMeshes(GrRenderTarget* renderTarget, const GrProgramInfo& programInfo,
+ const GrMesh meshes[], int meshCount) {
SkASSERT(meshCount); // guaranteed by GrOpsRenderPass::draw
- if (!this->flushGLState(renderTarget, programInfo)) {
- return;
- }
-
- if (GrPrimitiveType::kPatches == programInfo.primitiveType()) {
- this->flushPatchVertexCount(programInfo.tessellationPatchVertexCount());
- }
-
bool hasDynamicScissors = programInfo.hasDynamicScissors();
bool hasDynamicPrimProcTextures = programInfo.hasDynamicPrimProcTextures();
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 45a1ad8..c067d1e 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -73,10 +73,15 @@
// If the caller wishes to bind an index buffer to a specific VAO, it can call glBind directly.
GrGLenum bindBuffer(GrGpuBufferType type, const GrBuffer*);
+ // Flushes state from GrProgramInfo to GL. Returns false if the state couldn't be set.
+ bool flushGLState(GrRenderTarget*, const GrProgramInfo&);
+
// The GrGLOpsRenderPass does not buffer up draws before submitting them to the gpu.
// Thus this is the implementation of the draw call for the corresponding passthrough function
// on GrGLOpsRenderPass.
- void draw(GrRenderTarget*, const GrProgramInfo&, const GrMesh[], int meshCount);
+ //
+ // The client must call flushGLState before this method.
+ void drawMeshes(GrRenderTarget*, const GrProgramInfo&, const GrMesh[], int meshCount);
// GrMesh::SendToGpuImpl methods. These issue the actual GL draw calls.
// Marked final as a hint to the compiler to not use virtual dispatch.
@@ -284,9 +289,6 @@
// binds texture unit in GL
void setTextureUnit(int unitIdx);
- // Flushes state from GrPipeline to GL. Returns false if the state couldn't be set.
- bool flushGLState(GrRenderTarget*, const GrProgramInfo&);
-
void flushProgram(sk_sp<GrGLProgram>);
// Version for programs that aren't GrGLProgram.
diff --git a/src/gpu/gl/GrGLOpsRenderPass.h b/src/gpu/gl/GrGLOpsRenderPass.h
index 0685932..4be4899 100644
--- a/src/gpu/gl/GrGLOpsRenderPass.h
+++ b/src/gpu/gl/GrGLOpsRenderPass.h
@@ -49,9 +49,13 @@
private:
GrGpu* gpu() override { return fGpu; }
- void onDraw(const GrProgramInfo& programInfo, const GrMesh mesh[], int meshCount,
- const SkRect& bounds) override {
- fGpu->draw(fRenderTarget, programInfo, mesh, meshCount);
+ bool onBindPipeline(const GrProgramInfo& programInfo, const SkRect& drawBounds) override {
+ return fGpu->flushGLState(fRenderTarget, programInfo);
+ }
+
+ void onDrawMeshes(const GrProgramInfo& programInfo, const GrMesh mesh[],
+ int meshCount) override {
+ fGpu->drawMeshes(fRenderTarget, programInfo, mesh, meshCount);
}
void onClear(const GrFixedClip& clip, const SkPMColor4f& color) override {
diff --git a/src/gpu/mock/GrMockOpsRenderPass.h b/src/gpu/mock/GrMockOpsRenderPass.h
index d17774f..e392400 100644
--- a/src/gpu/mock/GrMockOpsRenderPass.h
+++ b/src/gpu/mock/GrMockOpsRenderPass.h
@@ -34,8 +34,10 @@
int numDraws() const { return fNumDraws; }
private:
- void onDraw(const GrProgramInfo&, const GrMesh[], int meshCount,
- const SkRect& bounds) override {
+ bool onBindPipeline(const GrProgramInfo&, const SkRect&) override {
+ return true;
+ }
+ void onDrawMeshes(const GrProgramInfo&, const GrMesh[], int meshCount) override {
this->markRenderTargetDirty();
++fNumDraws;
}
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.h b/src/gpu/mtl/GrMtlOpsRenderPass.h
index 30f19ca..8792eea 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.h
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.h
@@ -39,12 +39,9 @@
private:
GrGpu* gpu() override { return fGpu; }
- GrMtlPipelineState* prepareDrawState(const GrProgramInfo&);
+ bool onBindPipeline(const GrProgramInfo&, const SkRect& drawBounds) override;
- void onDraw(const GrProgramInfo& programInfo,
- const GrMesh mesh[],
- int meshCount,
- const SkRect& bounds) override;
+ void onDrawMeshes(const GrProgramInfo&, const GrMesh[], int meshCount) override;
void onClear(const GrFixedClip& clip, const SkPMColor4f& color) override;
@@ -76,6 +73,7 @@
GrMtlGpu* fGpu;
id<MTLRenderCommandEncoder> fActiveRenderCmdEncoder;
+ GrMtlPipelineState* fActivePipelineState = nullptr;
MTLRenderPassDescriptor* fRenderPassDesc;
SkRect fBounds;
size_t fCurrentVertexStride;
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.mm b/src/gpu/mtl/GrMtlOpsRenderPass.mm
index 41cef25..0f887d4 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.mm
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.mm
@@ -49,57 +49,38 @@
fActiveRenderCmdEncoder = nil;
}
-GrMtlPipelineState* GrMtlOpsRenderPass::prepareDrawState(const GrProgramInfo& programInfo) {
- // TODO: resolve textures and regenerate mipmaps as needed
-
- GrMtlPipelineState* pipelineState =
- fGpu->resourceProvider().findOrCreateCompatiblePipelineState(fRenderTarget, programInfo);
- if (!pipelineState) {
- return nullptr;
+bool GrMtlOpsRenderPass::onBindPipeline(const GrProgramInfo& programInfo,
+ const SkRect& drawBounds) {
+ fActivePipelineState = fGpu->resourceProvider().findOrCreateCompatiblePipelineState(
+ fRenderTarget, programInfo);
+ if (!fActivePipelineState) {
+ return false;
}
- pipelineState->setData(fRenderTarget, programInfo);
+ fActivePipelineState->setData(fRenderTarget, programInfo);
fCurrentVertexStride = programInfo.primProc().vertexStride();
- return pipelineState;
-}
-
-void GrMtlOpsRenderPass::onDraw(const GrProgramInfo& programInfo,
- const GrMesh meshes[],
- int meshCount,
- const SkRect& bounds) {
-
- SkASSERT(meshCount); // guaranteed by GrOpsRenderPass::draw
-
- GrMtlPipelineState* pipelineState = this->prepareDrawState(programInfo);
- if (!pipelineState) {
- return;
- }
-
if (!fActiveRenderCmdEncoder) {
fActiveRenderCmdEncoder =
fGpu->commandBuffer()->getRenderCommandEncoder(fRenderPassDesc, nullptr, this);
}
- [fActiveRenderCmdEncoder setRenderPipelineState:pipelineState->mtlPipelineState()];
- pipelineState->setDrawState(fActiveRenderCmdEncoder,
- programInfo.pipeline().outputSwizzle(),
- programInfo.pipeline().getXferProcessor());
+ [fActiveRenderCmdEncoder setRenderPipelineState:fActivePipelineState->mtlPipelineState()];
+ fActivePipelineState->setDrawState(fActiveRenderCmdEncoder,
+ programInfo.pipeline().outputSwizzle(),
+ programInfo.pipeline().getXferProcessor());
if (this->gpu()->caps()->wireframeMode() || programInfo.pipeline().isWireframe()) {
[fActiveRenderCmdEncoder setTriangleFillMode:MTLTriangleFillModeLines];
} else {
[fActiveRenderCmdEncoder setTriangleFillMode:MTLTriangleFillModeFill];
}
- bool hasDynamicScissors = programInfo.hasDynamicScissors();
- bool hasDynamicTextures = programInfo.hasDynamicPrimProcTextures();
-
if (!programInfo.pipeline().isScissorEnabled()) {
GrMtlPipelineState::SetDynamicScissorRectState(fActiveRenderCmdEncoder,
fRenderTarget, fOrigin,
SkIRect::MakeWH(fRenderTarget->width(),
fRenderTarget->height()));
- } else if (!hasDynamicScissors) {
+ } else if (!programInfo.hasDynamicScissors()) {
SkASSERT(programInfo.hasFixedScissor());
GrMtlPipelineState::SetDynamicScissorRectState(fActiveRenderCmdEncoder,
@@ -107,29 +88,35 @@
programInfo.fixedScissor());
}
- if (!hasDynamicTextures) {
- pipelineState->bindTextures(fActiveRenderCmdEncoder);
+ if (!programInfo.hasDynamicPrimProcTextures()) {
+ fActivePipelineState->bindTextures(fActiveRenderCmdEncoder);
}
+ fBounds.join(drawBounds);
+ return true;
+}
+
+void GrMtlOpsRenderPass::onDrawMeshes(const GrProgramInfo& programInfo, const GrMesh meshes[],
+ int meshCount) {
+ SkASSERT(fActivePipelineState);
+
for (int i = 0; i < meshCount; ++i) {
const GrMesh& mesh = meshes[i];
SkASSERT(nil != fActiveRenderCmdEncoder);
- if (hasDynamicScissors) {
+ if (programInfo.hasDynamicScissors()) {
GrMtlPipelineState::SetDynamicScissorRectState(fActiveRenderCmdEncoder, fRenderTarget,
fOrigin,
programInfo.dynamicScissor(i));
}
- if (hasDynamicTextures) {
+ if (programInfo.hasDynamicPrimProcTextures()) {
auto meshProxies = programInfo.dynamicPrimProcTextures(i);
- pipelineState->setTextures(programInfo, meshProxies);
- pipelineState->bindTextures(fActiveRenderCmdEncoder);
+ fActivePipelineState->setTextures(programInfo, meshProxies);
+ fActivePipelineState->bindTextures(fActiveRenderCmdEncoder);
}
mesh.sendToGpu(programInfo.primitiveType(), this);
}
-
- fBounds.join(bounds);
}
void GrMtlOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
diff --git a/src/gpu/ops/GrFillRRectOp.cpp b/src/gpu/ops/GrFillRRectOp.cpp
index e283813..7941f31 100644
--- a/src/gpu/ops/GrFillRRectOp.cpp
+++ b/src/gpu/ops/GrFillRRectOp.cpp
@@ -815,7 +815,8 @@
fBaseInstance, GrPrimitiveRestart::kNo);
mesh->setVertexData(std::move(fVertexBuffer));
- flushState->opsRenderPass()->draw(*fProgramInfo, mesh, 1, this->bounds());
+ flushState->opsRenderPass()->bindPipeline(*fProgramInfo, this->bounds());
+ flushState->opsRenderPass()->drawMeshes(*fProgramInfo, mesh, 1);
}
// Will the given corner look good if we use HW derivatives?
diff --git a/src/gpu/tessellate/GrDrawAtlasPathOp.cpp b/src/gpu/tessellate/GrDrawAtlasPathOp.cpp
index 1b2aa8a..34740d1 100644
--- a/src/gpu/tessellate/GrDrawAtlasPathOp.cpp
+++ b/src/gpu/tessellate/GrDrawAtlasPathOp.cpp
@@ -186,5 +186,6 @@
GrMesh mesh;
mesh.setInstanced(fInstanceBuffer, fInstanceCount, fBaseInstance, 4);
- state->opsRenderPass()->draw(programInfo, &mesh, 1, this->bounds());
+ state->opsRenderPass()->bindPipeline(programInfo, this->bounds());
+ state->opsRenderPass()->drawMeshes(programInfo, &mesh, 1);
}
diff --git a/src/gpu/tessellate/GrPathShader.h b/src/gpu/tessellate/GrPathShader.h
index facb9aa..e5a1f0f 100644
--- a/src/gpu/tessellate/GrPathShader.h
+++ b/src/gpu/tessellate/GrPathShader.h
@@ -48,7 +48,8 @@
state->proxy()->backendFormat(), state->view()->origin(),
pipeline, this, fixedDynamicState, nullptr, 0,
fPrimitiveType, fTessellationPatchVertexCount);
- state->opsRenderPass()->draw(programInfo, &mesh, 1, bounds);
+ state->opsRenderPass()->bindPipeline(programInfo, bounds);
+ state->opsRenderPass()->drawMeshes(programInfo, &mesh, 1);
}
private:
diff --git a/src/gpu/vk/GrVkOpsRenderPass.cpp b/src/gpu/vk/GrVkOpsRenderPass.cpp
index 08cc282..7de73cc 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.cpp
+++ b/src/gpu/vk/GrVkOpsRenderPass.cpp
@@ -479,50 +479,102 @@
}
}
-GrVkPipelineState* GrVkOpsRenderPass::prepareDrawState(
- const GrProgramInfo& programInfo,
- const SkIRect& renderPassScissorRect) {
+#ifdef SK_DEBUG
+void check_sampled_texture(GrTexture* tex, GrRenderTarget* rt, GrVkGpu* gpu) {
+ SkASSERT(!tex->isProtected() || (rt->isProtected() && gpu->protectedContext()));
+ GrVkTexture* vkTex = static_cast<GrVkTexture*>(tex);
+ SkASSERT(vkTex->currentLayout() == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+}
+
+void check_sampled_textures(const GrProgramInfo& programInfo, GrRenderTarget* rt, GrVkGpu* gpu) {
+ if (programInfo.hasDynamicPrimProcTextures()) {
+ for (int m = 0; m < programInfo.numDynamicStateArrays(); ++m) {
+ auto dynamicPrimProcTextures = programInfo.dynamicPrimProcTextures(m);
+
+ for (int s = 0; s < programInfo.primProc().numTextureSamplers(); ++s) {
+ auto texture = dynamicPrimProcTextures[s]->peekTexture();
+ check_sampled_texture(texture, rt, gpu);
+ }
+ }
+ } else if (programInfo.hasFixedPrimProcTextures()) {
+ auto fixedPrimProcTextures = programInfo.fixedPrimProcTextures();
+
+ for (int s = 0; s < programInfo.primProc().numTextureSamplers(); ++s) {
+ auto texture = fixedPrimProcTextures[s]->peekTexture();
+ check_sampled_texture(texture, rt, gpu);
+ }
+ }
+
+ GrFragmentProcessor::PipelineTextureSamplerRange textureSamplerRange(programInfo.pipeline());
+ for (auto [sampler, fp] : textureSamplerRange) {
+ check_sampled_texture(sampler.peekTexture(), rt, gpu);
+ }
+ if (GrTexture* dstTexture = programInfo.pipeline().peekDstTexture()) {
+ check_sampled_texture(dstTexture, rt, gpu);
+ }
+}
+#endif
+
+bool GrVkOpsRenderPass::onBindPipeline(const GrProgramInfo& programInfo, const SkRect& drawBounds) {
+ if (!fCurrentRenderPass) {
+ SkASSERT(fGpu->isDeviceLost());
+ return false;
+ }
+
+#ifdef SK_DEBUG
+ check_sampled_textures(programInfo, fRenderTarget, fGpu);
+
+ // Both the 'programInfo' and this renderPass have an origin. Since they come from the
+ // same place (i.e., the target renderTargetProxy) they had best agree.
+ SkASSERT(programInfo.origin() == fOrigin);
+#endif
+
+ SkRect rtRect = SkRect::Make(fBounds);
+ if (rtRect.intersect(drawBounds)) {
+ rtRect.roundOut(&fCurrentPipelineBounds);
+ } else {
+ fCurrentPipelineBounds.setEmpty();
+ }
+
GrVkCommandBuffer* currentCB = this->currentCommandBuffer();
SkASSERT(fCurrentRenderPass);
VkRenderPass compatibleRenderPass = fCurrentRenderPass->vkRenderPass();
- GrVkPipelineState* pipelineState =
- fGpu->resourceProvider().findOrCreateCompatiblePipelineState(fRenderTarget,
- programInfo,
- compatibleRenderPass);
- if (!pipelineState) {
- return pipelineState;
+ fCurrentPipelineState = fGpu->resourceProvider().findOrCreateCompatiblePipelineState(
+ fRenderTarget, programInfo, compatibleRenderPass);
+ if (!fCurrentPipelineState) {
+ return false;
}
- pipelineState->bindPipeline(fGpu, currentCB);
+ fCurrentPipelineState->bindPipeline(fGpu, currentCB);
// Both the 'programInfo' and this renderPass have an origin. Since they come from the
// same place (i.e., the target renderTargetProxy) they had best agree.
SkASSERT(programInfo.origin() == fOrigin);
- if (!pipelineState->setAndBindUniforms(fGpu, fRenderTarget, programInfo, currentCB)) {
- return nullptr;
+ if (!fCurrentPipelineState->setAndBindUniforms(fGpu, fRenderTarget, programInfo, currentCB)) {
+ return false;
}
// Check whether we need to bind textures between each GrMesh. If not we can bind them all now.
if (!programInfo.hasDynamicPrimProcTextures()) {
auto proxies = programInfo.hasFixedPrimProcTextures() ? programInfo.fixedPrimProcTextures()
: nullptr;
- if (!pipelineState->setAndBindTextures(fGpu, programInfo.primProc(), programInfo.pipeline(),
- proxies, currentCB)) {
- return nullptr;
+ if (!fCurrentPipelineState->setAndBindTextures(
+ fGpu, programInfo.primProc(), programInfo.pipeline(), proxies, currentCB)) {
+ return false;
}
}
if (!programInfo.pipeline().isScissorEnabled()) {
GrVkPipeline::SetDynamicScissorRectState(fGpu, currentCB, fRenderTarget, fOrigin,
- renderPassScissorRect);
+ fCurrentPipelineBounds);
} else if (!programInfo.hasDynamicScissors()) {
SkASSERT(programInfo.hasFixedScissor());
SkIRect combinedScissorRect;
- if (!combinedScissorRect.intersect(renderPassScissorRect, programInfo.fixedScissor())) {
+ if (!combinedScissorRect.intersect(fCurrentPipelineBounds, programInfo.fixedScissor())) {
combinedScissorRect = SkIRect::MakeEmpty();
}
GrVkPipeline::SetDynamicScissorRectState(fGpu, currentCB, fRenderTarget, fOrigin,
@@ -533,92 +585,36 @@
programInfo.pipeline().outputSwizzle(),
programInfo.pipeline().getXferProcessor());
- return pipelineState;
+ return true;
}
-#ifdef SK_DEBUG
-void check_sampled_texture(GrTexture* tex, GrRenderTarget* rt, GrVkGpu* gpu) {
- SkASSERT(!tex->isProtected() || (rt->isProtected() && gpu->protectedContext()));
- GrVkTexture* vkTex = static_cast<GrVkTexture*>(tex);
- SkASSERT(vkTex->currentLayout() == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
-}
-#endif
-
-
-void GrVkOpsRenderPass::onDraw(const GrProgramInfo& programInfo,
- const GrMesh meshes[], int meshCount,
- const SkRect& bounds) {
+void GrVkOpsRenderPass::onDrawMeshes(const GrProgramInfo& programInfo, const GrMesh meshes[],
+ int meshCount) {
if (!fCurrentRenderPass) {
SkASSERT(fGpu->isDeviceLost());
return;
}
+ SkASSERT(fCurrentPipelineState);
SkASSERT(meshCount); // guaranteed by GrOpsRenderPass::draw
-#ifdef SK_DEBUG
- if (programInfo.hasDynamicPrimProcTextures()) {
- for (int m = 0; m < meshCount; ++m) {
- auto dynamicPrimProcTextures = programInfo.dynamicPrimProcTextures(m);
-
- for (int s = 0; s < programInfo.primProc().numTextureSamplers(); ++s) {
- auto texture = dynamicPrimProcTextures[s]->peekTexture();
- check_sampled_texture(texture, fRenderTarget, fGpu);
- }
- }
- } else if (programInfo.hasFixedPrimProcTextures()) {
- auto fixedPrimProcTextures = programInfo.fixedPrimProcTextures();
-
- for (int s = 0; s < programInfo.primProc().numTextureSamplers(); ++s) {
- auto texture = fixedPrimProcTextures[s]->peekTexture();
- check_sampled_texture(texture, fRenderTarget, fGpu);
- }
- }
-
- GrFragmentProcessor::PipelineTextureSamplerRange textureSamplerRange(programInfo.pipeline());
- for (auto [sampler, fp] : textureSamplerRange) {
- check_sampled_texture(sampler.peekTexture(), fRenderTarget, fGpu);
- }
- if (GrTexture* dstTexture = programInfo.pipeline().peekDstTexture()) {
- check_sampled_texture(dstTexture, fRenderTarget, fGpu);
- }
-
- // Both the 'programInfo' and this renderPass have an origin. Since they come from the
- // same place (i.e., the target renderTargetProxy) they had best agree.
- SkASSERT(programInfo.origin() == fOrigin);
-#endif
-
- SkRect scissorRect = SkRect::Make(fBounds);
- SkIRect renderPassScissorRect = SkIRect::MakeEmpty();
- if (scissorRect.intersect(bounds)) {
- scissorRect.roundOut(&renderPassScissorRect);
- }
-
- GrVkPipelineState* pipelineState = this->prepareDrawState(programInfo, renderPassScissorRect);
- if (!pipelineState) {
- return;
- }
-
- bool hasDynamicScissors = programInfo.hasDynamicScissors();
- bool hasDynamicTextures = programInfo.hasDynamicPrimProcTextures();
-
for (int i = 0; i < meshCount; ++i) {
const GrMesh& mesh = meshes[i];
- if (hasDynamicScissors) {
+ if (programInfo.hasDynamicScissors()) {
SkIRect combinedScissorRect;
- if (!combinedScissorRect.intersect(renderPassScissorRect,
+ if (!combinedScissorRect.intersect(fCurrentPipelineBounds,
programInfo.dynamicScissor(i))) {
combinedScissorRect = SkIRect::MakeEmpty();
}
GrVkPipeline::SetDynamicScissorRectState(fGpu, this->currentCommandBuffer(),
- fRenderTarget, fOrigin,
- combinedScissorRect);
+ fRenderTarget, fOrigin, combinedScissorRect);
}
- if (hasDynamicTextures) {
+ if (programInfo.hasDynamicPrimProcTextures()) {
auto meshProxies = programInfo.dynamicPrimProcTextures(i);
- if (!pipelineState->setAndBindTextures(fGpu, programInfo.primProc(),
- programInfo.pipeline(), meshProxies,
- this->currentCommandBuffer())) {
+ if (!fCurrentPipelineState->setAndBindTextures(fGpu, programInfo.primProc(),
+ programInfo.pipeline(), meshProxies,
+ this->currentCommandBuffer())) {
if (fGpu->isDeviceLost()) {
return;
} else {
@@ -626,7 +622,6 @@
}
}
}
- SkASSERT(pipelineState);
mesh.sendToGpu(programInfo.primitiveType(), this);
}
@@ -664,7 +659,7 @@
////////////////////////////////////////////////////////////////////////////////
-void GrVkOpsRenderPass::executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable) {
+void GrVkOpsRenderPass::onExecuteDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable) {
if (!fCurrentRenderPass) {
SkASSERT(fGpu->isDeviceLost());
return;
diff --git a/src/gpu/vk/GrVkOpsRenderPass.h b/src/gpu/vk/GrVkOpsRenderPass.h
index 64209d9..48e3df7 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.h
+++ b/src/gpu/vk/GrVkOpsRenderPass.h
@@ -34,7 +34,7 @@
void inlineUpload(GrOpFlushState* state, GrDeferredTextureUploadFn& upload) override;
- void executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) override;
+ void onExecuteDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) override;
bool set(GrRenderTarget*, GrSurfaceOrigin, const SkIRect& bounds,
const GrOpsRenderPass::LoadAndStoreInfo&,
@@ -68,10 +68,9 @@
const GrGpuBuffer* vertexBuffer,
const GrGpuBuffer* instanceBuffer);
- GrVkPipelineState* prepareDrawState(const GrProgramInfo&, const SkIRect& renderPassScissorRect);
+ bool onBindPipeline(const GrProgramInfo&, const SkRect& drawBounds) override;
- void onDraw(const GrProgramInfo&, const GrMesh[], int meshCount,
- const SkRect& bounds) override;
+ void onDrawMeshes(const GrProgramInfo&, const GrMesh[], int meshCount) override;
// GrMesh::SendToGpuImpl methods. These issue the actual Vulkan draw commands.
// Marked final as a hint to the compiler to not use virtual dispatch.
@@ -101,6 +100,8 @@
std::unique_ptr<GrVkSecondaryCommandBuffer> fCurrentSecondaryCommandBuffer;
const GrVkRenderPass* fCurrentRenderPass;
+ SkIRect fCurrentPipelineBounds;
+ GrVkPipelineState* fCurrentPipelineState = nullptr;
bool fCurrentCBIsEmpty = true;
SkIRect fBounds;
GrVkGpu* fGpu;
diff --git a/tests/GrMeshTest.cpp b/tests/GrMeshTest.cpp
index 8a505a8..8142aa8 100644
--- a/tests/GrMeshTest.cpp
+++ b/tests/GrMeshTest.cpp
@@ -446,8 +446,8 @@
mtp,
nullptr, nullptr, 0, primitiveType);
- fState->opsRenderPass()->draw(programInfo, &mesh, 1,
- SkRect::MakeIWH(kImageWidth, kImageHeight));
+ fState->opsRenderPass()->bindPipeline(programInfo, SkRect::MakeIWH(kImageWidth, kImageHeight));
+ fState->opsRenderPass()->drawMeshes(programInfo, &mesh, 1);
}
static void run_test(GrContext* context, const char* testName, skiatest::Reporter* reporter,
diff --git a/tests/GrPipelineDynamicStateTest.cpp b/tests/GrPipelineDynamicStateTest.cpp
index e62269b..a5ce0a2 100644
--- a/tests/GrPipelineDynamicStateTest.cpp
+++ b/tests/GrPipelineDynamicStateTest.cpp
@@ -169,8 +169,9 @@
nullptr,
&dynamicState, 0, GrPrimitiveType::kTriangleStrip);
- flushState->opsRenderPass()->draw(programInfo, meshes.begin(), 4,
- SkRect::MakeIWH(kScreenSize, kScreenSize));
+ flushState->opsRenderPass()->bindPipeline(programInfo,
+ SkRect::MakeIWH(kScreenSize, kScreenSize));
+ flushState->opsRenderPass()->drawMeshes(programInfo, meshes.begin(), 4);
}
GrScissorTest fScissorTest;