Centralize error checking w/in the GrProgramInfo class

Although not necessary I would like to centralize/encapsulate the error checking so there is just one place to go to update/extend this code. It does have the nice property that, since we validate in the ctor and this object is immutable, we only have to do it once (and can delete some code spread throughout the code base).

Bug: skia:9455
Change-Id: Iaa26c7a896a9bd053358a6f8424d7f5944b0117e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/247339
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index a612881..ae5ebb0 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -142,6 +142,7 @@
   "$_src/gpu/GrProcessorSet.h",
   "$_src/gpu/GrProgramDesc.cpp",
   "$_src/gpu/GrProgramDesc.h",
+  "$_src/gpu/GrProgramInfo.cpp",
   "$_src/gpu/GrProgramInfo.h",
   "$_src/gpu/GrProcessor.cpp",
   "$_src/gpu/GrProcessor.h",
diff --git a/src/gpu/GrOpsRenderPass.cpp b/src/gpu/GrOpsRenderPass.cpp
index fd4345b..548bbb4 100644
--- a/src/gpu/GrOpsRenderPass.cpp
+++ b/src/gpu/GrOpsRenderPass.cpp
@@ -35,49 +35,6 @@
     this->onClearStencilClip(clip, insideStencilMask);
 }
 
-#ifdef SK_DEBUG
-static void assert_msaa_and_mips_are_resolved(const GrProgramInfo& programInfo, int meshCount) {
-    auto assertResolved = [](GrTexture* tex, const GrSamplerState& sampler) {
-        SkASSERT(tex);
-
-        // Ensure mipmaps were all resolved ahead of time by the DAG.
-        if (GrSamplerState::Filter::kMipMap == sampler.filter() &&
-            (tex->width() != 1 || tex->height() != 1)) {
-            // There are some cases where we might be given a non-mipmapped texture with a mipmap
-            // filter. See skbug.com/7094.
-            SkASSERT(tex->texturePriv().mipMapped() != GrMipMapped::kYes ||
-                     !tex->texturePriv().mipMapsAreDirty());
-        }
-    };
-
-    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* tex = dynamicPrimProcTextures[s]->peekTexture();
-                assertResolved(tex, programInfo.primProc().textureSampler(s).samplerState());
-            }
-        }
-    } else if (programInfo.hasFixedPrimProcTextures()) {
-        auto fixedPrimProcTextures = programInfo.fixedPrimProcTextures();
-
-        for (int s = 0; s < programInfo.primProc().numTextureSamplers(); ++s) {
-            auto* tex = fixedPrimProcTextures[s]->peekTexture();
-            assertResolved(tex, programInfo.primProc().textureSampler(s).samplerState());
-        }
-    }
-
-    GrFragmentProcessor::Iter iter(programInfo.pipeline());
-    while (const GrFragmentProcessor* fp = iter.next()) {
-        for (int s = 0; s < fp->numTextureSamplers(); ++s) {
-            const auto& textureSampler = fp->textureSampler(s);
-            assertResolved(textureSampler.peekTexture(), textureSampler.samplerState());
-        }
-    }
-}
-#endif
-
 bool GrOpsRenderPass::draw(const GrProgramInfo& programInfo,
                            const GrMesh meshes[], int meshCount, const SkRect& bounds) {
     if (!meshCount) {
@@ -87,52 +44,14 @@
 #ifdef SK_DEBUG
     SkASSERT(!programInfo.primProc().hasInstanceAttributes() ||
              this->gpu()->caps()->instanceAttribSupport());
+
     for (int i = 0; i < meshCount; ++i) {
         SkASSERT(programInfo.primProc().hasVertexAttributes() == meshes[i].hasVertexData());
         SkASSERT(programInfo.primProc().hasInstanceAttributes() == meshes[i].hasInstanceData());
     }
 
-    SkASSERT(!programInfo.pipeline().isScissorEnabled() || programInfo.fixedDynamicState() ||
-             (programInfo.dynamicStateArrays() && programInfo.dynamicStateArrays()->fScissorRects));
-
-    SkASSERT(!programInfo.pipeline().isBad());
-
-    if (programInfo.hasFixedPrimProcTextures()) {
-        auto fixedPrimProcTextures = programInfo.fixedPrimProcTextures();
-        for (int s = 0; s < programInfo.primProc().numTextureSamplers(); ++s) {
-            SkASSERT(fixedPrimProcTextures[s]->isInstantiated());
-        }
-    }
-
-    if (programInfo.hasDynamicPrimProcTextures()) {
-        for (int m = 0; m < meshCount; ++m) {
-            auto dynamicPrimProcTextures = programInfo.dynamicPrimProcTextures(m);
-            for (int s = 0; s < programInfo.primProc().numTextureSamplers(); ++s) {
-                SkASSERT(dynamicPrimProcTextures[s]->isInstantiated());
-            }
-        }
-
-        // Check that, for a given sampler, the properties of the dynamic textures remain
-        // the same for all the meshes
-        for (int s = 0; s < programInfo.primProc().numTextureSamplers(); ++s) {
-            auto dynamicPrimProcTextures = programInfo.dynamicPrimProcTextures(0);
-
-            const GrBackendFormat& format = dynamicPrimProcTextures[s]->backendFormat();
-            GrTextureType type = dynamicPrimProcTextures[s]->textureType();
-            GrPixelConfig config = dynamicPrimProcTextures[s]->config();
-
-            for (int m = 1; m < meshCount; ++m) {
-                dynamicPrimProcTextures = programInfo.dynamicPrimProcTextures(m);
-
-                auto testProxy = dynamicPrimProcTextures[s];
-                SkASSERT(testProxy->backendFormat() == format);
-                SkASSERT(testProxy->textureType() == type);
-                SkASSERT(testProxy->config() == config);
-            }
-        }
-    }
-
-    assert_msaa_and_mips_are_resolved(programInfo, meshCount);
+    programInfo.checkAllInstantiated(meshCount);
+    programInfo.checkMSAAAndMIPSAreResolved(meshCount);
 #endif
 
     if (programInfo.primProc().numVertexAttributes() > this->gpu()->caps()->maxVertexAttributes()) {
diff --git a/src/gpu/GrProgramInfo.cpp b/src/gpu/GrProgramInfo.cpp
new file mode 100644
index 0000000..0abc7c2
--- /dev/null
+++ b/src/gpu/GrProgramInfo.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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/GrProgramInfo.h"
+
+
+#ifdef SK_DEBUG
+#include "src/gpu/GrTexturePriv.h"
+
+void GrProgramInfo::validate() const {
+    SkASSERT(!fPipeline.isBad());
+
+    if (this->hasDynamicPrimProcTextures()) {
+        SkASSERT(!this->hasFixedPrimProcTextures());
+        SkASSERT(fPrimProc.numTextureSamplers());
+    } else if (this->hasFixedPrimProcTextures()) {
+        SkASSERT(fPrimProc.numTextureSamplers());
+    } else {
+        SkASSERT(!fPrimProc.numTextureSamplers());
+    }
+
+    SkASSERT(!fPipeline.isScissorEnabled() || this->hasFixedScissor() ||
+             this->hasDynamicScissors());
+}
+
+void GrProgramInfo::checkAllInstantiated(int meshCount) const {
+    if (this->hasFixedPrimProcTextures()) {
+        auto fixedPrimProcTextures = this->fixedPrimProcTextures();
+        for (int s = 0; s < this->primProc().numTextureSamplers(); ++s) {
+            SkASSERT(fixedPrimProcTextures[s]->isInstantiated());
+        }
+    }
+
+    if (this->hasDynamicPrimProcTextures()) {
+        for (int m = 0; m < meshCount; ++m) {
+            auto dynamicPrimProcTextures = this->dynamicPrimProcTextures(m);
+            for (int s = 0; s < this->primProc().numTextureSamplers(); ++s) {
+                SkASSERT(dynamicPrimProcTextures[s]->isInstantiated());
+            }
+        }
+
+        // TODO: if GrProgramInfo had the mesh count we could do this in validate!
+        // Check that, for a given sampler, the properties of the dynamic textures remain
+        // the same for all the meshes
+        for (int s = 0; s < this->primProc().numTextureSamplers(); ++s) {
+            auto dynamicPrimProcTextures = this->dynamicPrimProcTextures(0);
+
+            const GrBackendFormat& format = dynamicPrimProcTextures[s]->backendFormat();
+            GrTextureType type = dynamicPrimProcTextures[s]->textureType();
+            GrPixelConfig config = dynamicPrimProcTextures[s]->config();
+
+            for (int m = 1; m < meshCount; ++m) {
+                dynamicPrimProcTextures = this->dynamicPrimProcTextures(m);
+
+                auto testProxy = dynamicPrimProcTextures[s];
+                SkASSERT(testProxy->backendFormat() == format);
+                SkASSERT(testProxy->textureType() == type);
+                SkASSERT(testProxy->config() == config);
+            }
+        }
+    }
+}
+
+void GrProgramInfo::checkMSAAAndMIPSAreResolved(int meshCount) const {
+
+    auto assertResolved = [](GrTexture* tex, const GrSamplerState& sampler) {
+        SkASSERT(tex);
+
+        // Ensure mipmaps were all resolved ahead of time by the DAG.
+        if (GrSamplerState::Filter::kMipMap == sampler.filter() &&
+            (tex->width() != 1 || tex->height() != 1)) {
+            // There are some cases where we might be given a non-mipmapped texture with a mipmap
+            // filter. See skbug.com/7094.
+            SkASSERT(tex->texturePriv().mipMapped() != GrMipMapped::kYes ||
+                     !tex->texturePriv().mipMapsAreDirty());
+        }
+    };
+
+    if (this->hasDynamicPrimProcTextures()) {
+        for (int m = 0; m < meshCount; ++m) {
+            auto dynamicPrimProcTextures = this->dynamicPrimProcTextures(m);
+
+            for (int s = 0; s < this->primProc().numTextureSamplers(); ++s) {
+                auto* tex = dynamicPrimProcTextures[s]->peekTexture();
+                assertResolved(tex, this->primProc().textureSampler(s).samplerState());
+            }
+        }
+    } else if (this->hasFixedPrimProcTextures()) {
+        auto fixedPrimProcTextures = this->fixedPrimProcTextures();
+
+        for (int s = 0; s < this->primProc().numTextureSamplers(); ++s) {
+            auto* tex = fixedPrimProcTextures[s]->peekTexture();
+            assertResolved(tex, this->primProc().textureSampler(s).samplerState());
+        }
+    }
+
+    GrFragmentProcessor::Iter iter(this->pipeline());
+    while (const GrFragmentProcessor* fp = iter.next()) {
+        for (int s = 0; s < fp->numTextureSamplers(); ++s) {
+            const auto& textureSampler = fp->textureSampler(s);
+            assertResolved(textureSampler.peekTexture(), textureSampler.samplerState());
+        }
+    }
+}
+
+#endif
diff --git a/src/gpu/GrProgramInfo.h b/src/gpu/GrProgramInfo.h
index ec1d3b3..fd2672f 100644
--- a/src/gpu/GrProgramInfo.h
+++ b/src/gpu/GrProgramInfo.h
@@ -14,6 +14,9 @@
 
 class GrProgramInfo {
 public:
+    // TODO: it seems like this object should also get the number of copies in
+    // dynamicStateArrays. If that were true a portion of checkAllInstantiated could be moved
+    // to validate.
     GrProgramInfo(int numSamples,
                   GrSurfaceOrigin origin,
                   const GrPipeline& pipeline,
@@ -26,6 +29,7 @@
             , fPrimProc(primProc)
             , fFixedDynamicState(fixedDynamicState)
             , fDynamicStateArrays(dynamicStateArrays) {
+        SkDEBUGCODE(this->validate();)
     }
 
     int numSamples() const { return fNumSamples;  }
@@ -89,6 +93,10 @@
     }
 
 #ifdef SK_DEBUG
+    void validate() const;
+    void checkAllInstantiated(int meshCount) const;
+    void checkMSAAAndMIPSAreResolved(int meshCount) const;
+
     bool isNVPR() const {
         return fPrimProc.isPathRendering() && !fPrimProc.willUseGeoShader() &&
                !fPrimProc.numVertexAttributes() && !fPrimProc.numInstanceAttributes();
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 07ea39c..3ce3267 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -50,8 +50,6 @@
                                                GrProgramDesc* desc,
                                                GrGLGpu* gpu,
                                                const GrGLPrecompiledProgram* precompiledProgram) {
-    SkASSERT(!programInfo.pipeline().isBad());
-
     ATRACE_ANDROID_FRAMEWORK("Shader Compile");
     GrAutoLocaleSetter als("C");
 
diff --git a/src/gpu/mtl/GrMtlPipelineState.mm b/src/gpu/mtl/GrMtlPipelineState.mm
index 96ecb18..e08ada7 100644
--- a/src/gpu/mtl/GrMtlPipelineState.mm
+++ b/src/gpu/mtl/GrMtlPipelineState.mm
@@ -59,7 +59,6 @@
 
 void GrMtlPipelineState::setData(const GrRenderTarget* renderTarget,
                                  const GrProgramInfo& programInfo) {
-    SkASSERT(programInfo.primProcProxies() || !programInfo.primProc().numTextureSamplers());
 
     // Note: the Metal backend currently only supports fixed primProc textures
     const GrTextureProxy* const* primProcProxies = programInfo.primProcProxies();