Adds gpu stats for program cache

With the addition of the DDL program pre-compilation we need to know how it is working.

This CL also fixes some threading bugs.

Bug: skia:9455
Change-Id: I20da58a7f1b19685687fae1d159d4e0db8a4964d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/273001
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/core/SkDeferredDisplayList.cpp b/src/core/SkDeferredDisplayList.cpp
index 112f577..f14aa54 100644
--- a/src/core/SkDeferredDisplayList.cpp
+++ b/src/core/SkDeferredDisplayList.cpp
@@ -23,12 +23,18 @@
                                              sk_sp<LazyProxyData> lazyProxyData)
         : fCharacterization(characterization)
 #if SK_SUPPORT_GPU
-        , fLazyProxyData(std::move(lazyProxyData))
+    , fLazyProxyData(std::move(lazyProxyData))
 #endif
 {
 }
 
-SkDeferredDisplayList::~SkDeferredDisplayList() {}
+SkDeferredDisplayList::~SkDeferredDisplayList() {
+#if SK_SUPPORT_GPU && defined(SK_DEBUG)
+    for (auto& renderTask : fRenderTasks) {
+        SkASSERT(renderTask->unique());
+    }
+#endif
+}
 
 //-------------------------------------------------------------------------------------------------
 #if SK_SUPPORT_GPU
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index af1440b..6e919da 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -104,6 +104,12 @@
 }
 
 void GrDrawingManager::RenderTaskDAG::add(const SkTArray<sk_sp<GrRenderTask>>& renderTasks) {
+#ifdef SK_DEBUG
+    for (auto& renderTask : renderTasks) {
+        SkASSERT(renderTask->unique());
+    }
+#endif
+
     fRenderTasks.push_back_n(renderTasks.count(), renderTasks.begin());
 }
 
@@ -576,8 +582,9 @@
     fActiveOpsTask = nullptr;
 
     fDAG.swap(&ddl->fRenderTasks);
+    SkASSERT(!fDAG.numRenderTasks());
 
-    for (auto renderTask : ddl->fRenderTasks) {
+    for (auto& renderTask : ddl->fRenderTasks) {
         renderTask->prePrepare(fContext);
     }
 
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 0eca489..d6b97a2 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -702,6 +702,19 @@
 #if GR_TEST_UTILS
 
 #if GR_GPU_STATS
+static const char* cache_result_to_str(int i) {
+    const char* kCacheResultStrings[GrGpu::Stats::kNumProgramCacheResults] = {
+        "hits",
+        "misses",
+        "partials"
+    };
+    static_assert(0 == (int) GrGpu::Stats::ProgramCacheResult::kHit);
+    static_assert(1 == (int) GrGpu::Stats::ProgramCacheResult::kMiss);
+    static_assert(2 == (int) GrGpu::Stats::ProgramCacheResult::kPartial);
+    static_assert(GrGpu::Stats::kNumProgramCacheResults == 3);
+    return kCacheResultStrings[i];
+}
+
 void GrGpu::Stats::dump(SkString* out) {
     out->appendf("Render Target Binds: %d\n", fRenderTargetBinds);
     out->appendf("Shader Compilations: %d\n", fShaderCompilations);
@@ -712,6 +725,26 @@
     out->appendf("Stencil Buffer Creates: %d\n", fStencilAttachmentCreates);
     out->appendf("Number of draws: %d\n", fNumDraws);
     out->appendf("Number of Scratch Textures reused %d\n", fNumScratchTexturesReused);
+
+    SkASSERT(fNumInlineCompilationFailures == 0);
+    out->appendf("Number of Inline compile failures %d\n", fNumInlineCompilationFailures);
+    for (int i = 0; i < Stats::kNumProgramCacheResults-1; ++i) {
+        out->appendf("Inline Program Cache %s %d\n", cache_result_to_str(i),
+                     fInlineProgramCacheStats[i]);
+    }
+
+    SkASSERT(fNumPreCompilationFailures == 0);
+    out->appendf("Number of precompile failures %d\n", fNumPreCompilationFailures);
+    for (int i = 0; i < Stats::kNumProgramCacheResults-1; ++i) {
+        out->appendf("Precompile Program Cache %s %d\n", cache_result_to_str(i),
+                     fPreProgramCacheStats[i]);
+    }
+
+    SkASSERT(fNumCompilationFailures == 0);
+    out->appendf("Total number of compilation failures %d\n", fNumCompilationFailures);
+    out->appendf("Total number of partial compilation successes %d\n",
+                 fNumPartialCompilationSuccesses);
+    out->appendf("Total number of compilation successes %d\n", fNumCompilationSuccesses);
 }
 
 void GrGpu::Stats::dumpKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* values) {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index c0a1bc1..aa10bd8 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -386,6 +386,16 @@
 
     class Stats {
     public:
+        enum class ProgramCacheResult {
+            kHit,       // the program was found in the cache
+            kMiss,      // the program was not found in the cache (and was, thus, compiled)
+            kPartial,   // a precompiled version was found in the persistent cache
+
+            kLast = kPartial
+        };
+
+        static const int kNumProgramCacheResults = (int)ProgramCacheResult::kLast + 1;
+
 #if GR_GPU_STATS
         Stats() = default;
 
@@ -424,6 +434,35 @@
         int numScratchTexturesReused() const { return fNumScratchTexturesReused; }
         void incNumScratchTexturesReused() { ++fNumScratchTexturesReused; }
 
+        int numInlineCompilationFailures() const { return fNumInlineCompilationFailures; }
+        void incNumInlineCompilationFailures() { ++fNumInlineCompilationFailures; }
+
+        int numInlineProgramCacheResult(ProgramCacheResult stat) const {
+            return fInlineProgramCacheStats[(int) stat];
+        }
+        void incNumInlineProgramCacheResult(ProgramCacheResult stat) {
+            ++fInlineProgramCacheStats[(int) stat];
+        }
+
+        int numPreCompilationFailures() const { return fNumPreCompilationFailures; }
+        void incNumPreCompilationFailures() { ++fNumPreCompilationFailures; }
+
+        int numPreProgramCacheResult(ProgramCacheResult stat) const {
+            return fPreProgramCacheStats[(int) stat];
+        }
+        void incNumPreProgramCacheResult(ProgramCacheResult stat) {
+            ++fPreProgramCacheStats[(int) stat];
+        }
+
+        int numCompilationFailures() const { return fNumCompilationFailures; }
+        void incNumCompilationFailures() { ++fNumCompilationFailures; }
+
+        int numPartialCompilationSuccesses() const { return fNumPartialCompilationSuccesses; }
+        void incNumPartialCompilationSuccesses() { ++fNumPartialCompilationSuccesses; }
+
+        int numCompilationSuccesses() const { return fNumCompilationSuccesses; }
+        void incNumCompilationSuccesses() { ++fNumCompilationSuccesses; }
+
 #if GR_TEST_UTILS
         void dump(SkString*);
         void dumpKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* values);
@@ -440,6 +479,17 @@
         int fNumFailedDraws = 0;
         int fNumFinishFlushes = 0;
         int fNumScratchTexturesReused = 0;
+
+        int fNumInlineCompilationFailures = 0;
+        int fInlineProgramCacheStats[kNumProgramCacheResults] = { 0 };
+
+        int fNumPreCompilationFailures = 0;
+        int fPreProgramCacheStats[kNumProgramCacheResults] = { 0 };
+
+        int fNumCompilationFailures = 0;
+        int fNumPartialCompilationSuccesses = 0;
+        int fNumCompilationSuccesses = 0;
+
 #else
 
 #if GR_TEST_UTILS
@@ -455,6 +505,13 @@
         void incNumDraws() {}
         void incNumFailedDraws() {}
         void incNumFinishFlushes() {}
+        void incNumInlineCompilationFailures() {}
+        void incNumInlineProgramCacheResult(ProgramCacheResult stat) {}
+        void incNumPreCompilationFailures() {}
+        void incNumPreProgramCacheResult(ProgramCacheResult stat) {}
+        void incNumCompilationFailures() {}
+        void incNumPartialCompilationSuccesses() {}
+        void incNumCompilationSuccesses() {}
 #endif
     };
 
diff --git a/src/gpu/GrMemoryPool.cpp b/src/gpu/GrMemoryPool.cpp
index b2f627d..b5249e1 100644
--- a/src/gpu/GrMemoryPool.cpp
+++ b/src/gpu/GrMemoryPool.cpp
@@ -51,7 +51,7 @@
     int n = fAllocatedIDs.count();
     fAllocatedIDs.foreach([&i, n] (int32_t id) {
         if (++i == 1) {
-            SkDebugf("Leaked IDs (in no particular order): %d", id);
+            SkDebugf("Leaked %d IDs (in no particular order): %d%s", n, id, (n == i) ? "\n" : "");
         } else if (i < 11) {
             SkDebugf(", %d%s", id, (n == i ? "\n" : ""));
         } else if (i == 11) {
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 315006b..de2c53f 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -333,7 +333,15 @@
         sk_sp<GrGLProgram> findOrCreateProgram(GrRenderTarget*, const GrProgramInfo&);
         sk_sp<GrGLProgram> findOrCreateProgram(const GrProgramDesc& desc,
                                                const GrProgramInfo& programInfo) {
-            return this->findOrCreateProgram(nullptr, desc, programInfo);
+            Stats::ProgramCacheResult stat;
+            sk_sp<GrGLProgram> tmp = this->findOrCreateProgram(nullptr, desc, programInfo, &stat);
+            if (!tmp) {
+                fGpu->fStats.incNumPreCompilationFailures();
+            } else {
+                fGpu->fStats.incNumPreProgramCacheResult(stat);
+            }
+
+            return tmp;
         }
         bool precompileShader(const SkData& key, const SkData& data);
 
@@ -342,7 +350,8 @@
 
         sk_sp<GrGLProgram> findOrCreateProgram(GrRenderTarget*,
                                                const GrProgramDesc&,
-                                               const GrProgramInfo&);
+                                               const GrProgramInfo&,
+                                               Stats::ProgramCacheResult*);
 
         struct DescHash {
             uint32_t operator()(const GrProgramDesc& desc) const {
diff --git a/src/gpu/gl/GrGLGpuProgramCache.cpp b/src/gpu/gl/GrGLGpuProgramCache.cpp
index 02c75a1..bf54a88 100644
--- a/src/gpu/gl/GrGLGpuProgramCache.cpp
+++ b/src/gpu/gl/GrGLGpuProgramCache.cpp
@@ -55,12 +55,22 @@
         return nullptr;
     }
 
-    return this->findOrCreateProgram(renderTarget, desc, programInfo);
+    Stats::ProgramCacheResult stat;
+    sk_sp<GrGLProgram> tmp = this->findOrCreateProgram(renderTarget, desc, programInfo, &stat);
+    if (!tmp) {
+        fGpu->fStats.incNumInlineCompilationFailures();
+    } else {
+        fGpu->fStats.incNumInlineProgramCacheResult(stat);
+    }
+
+    return tmp;
 }
 
 sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgram(GrRenderTarget* renderTarget,
                                                               const GrProgramDesc& desc,
-                                                              const GrProgramInfo& programInfo) {
+                                                              const GrProgramInfo& programInfo,
+                                                              Stats::ProgramCacheResult* stat) {
+    *stat = Stats::ProgramCacheResult::kHit;
     std::unique_ptr<Entry>* entry = fMap.find(desc);
     if (entry && !(*entry)->fProgram) {
         // We've pre-compiled the GL program, but don't have the GrGLProgram scaffolding
@@ -71,16 +81,22 @@
         if (!(*entry)->fProgram) {
             // Should we purge the program ID from the cache at this point?
             SkDEBUGFAIL("Couldn't create program from precompiled program");
+            fGpu->fStats.incNumCompilationFailures();
             return nullptr;
         }
+        fGpu->fStats.incNumPartialCompilationSuccesses();
+        *stat = Stats::ProgramCacheResult::kPartial;
     } else if (!entry) {
         // We have a cache miss
         sk_sp<GrGLProgram> program = GrGLProgramBuilder::CreateProgram(fGpu, renderTarget,
                                                                        desc, programInfo);
         if (!program) {
+            fGpu->fStats.incNumCompilationFailures();
             return nullptr;
         }
+        fGpu->fStats.incNumCompilationSuccesses();
         entry = fMap.insert(desc, std::unique_ptr<Entry>(new Entry(std::move(program))));
+        *stat = Stats::ProgramCacheResult::kMiss;
     }
 
     return (*entry)->fProgram;