Add d3d pipeline state cache

Change-Id: Iab9d4288a54d0743dfbf94f078b206e5df3b5f87
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/287378
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/d3d/GrD3DResourceProvider.cpp b/src/gpu/d3d/GrD3DResourceProvider.cpp
index 67bc060..5cc2783 100644
--- a/src/gpu/d3d/GrD3DResourceProvider.cpp
+++ b/src/gpu/d3d/GrD3DResourceProvider.cpp
@@ -7,10 +7,16 @@
 
 #include "src/gpu/d3d/GrD3DResourceProvider.h"
 
+#include "include/gpu/GrContextOptions.h"
+#include "src/gpu/GrContextPriv.h"
 #include "src/gpu/d3d/GrD3DCommandList.h"
 #include "src/gpu/d3d/GrD3DGpu.h"
+#include "src/gpu/d3d/GrD3DPipelineState.h"
+#include "src/gpu/d3d/GrD3DPipelineStateBuilder.h"
 
-GrD3DResourceProvider::GrD3DResourceProvider(GrD3DGpu* gpu) : fGpu(gpu) {
+GrD3DResourceProvider::GrD3DResourceProvider(GrD3DGpu* gpu)
+        : fGpu(gpu)
+        , fPipelineStateCache(new PipelineStateCache(gpu)) {
     // TODO: Change to handle growing the heap rather than a fixed size
     const int kMaxRenderTargetViews = 256;
 
@@ -61,3 +67,78 @@
 void GrD3DResourceProvider::recycleRenderTargetView(D3D12_CPU_DESCRIPTOR_HANDLE* rtvDescriptor) {
     fRTVDescriptorHeap->freeCPUHandle(rtvDescriptor);
 }
+
+sk_sp<GrD3DPipelineState> GrD3DResourceProvider::findOrCreateCompatiblePipelineState(
+        GrRenderTarget* rt, const GrProgramInfo& info) {
+    return fPipelineStateCache->refPipelineState(rt, info);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_PIPELINE_STATE_CACHE_STATS
+// Display pipeline state cache usage
+static const bool c_DisplayMtlPipelineCache{false};
+#endif
+
+struct GrD3DResourceProvider::PipelineStateCache::Entry {
+    Entry(GrD3DGpu* gpu, sk_sp<GrD3DPipelineState> pipelineState)
+            : fGpu(gpu), fPipelineState(std::move(pipelineState)) {}
+
+    GrD3DGpu* fGpu;
+    sk_sp<GrD3DPipelineState> fPipelineState;
+};
+
+GrD3DResourceProvider::PipelineStateCache::PipelineStateCache(GrD3DGpu* gpu)
+        : fMap(gpu->getContext()->priv().options().fRuntimeProgramCacheSize)
+        , fGpu(gpu)
+#ifdef GR_PIPELINE_STATE_CACHE_STATS
+        , fTotalRequests(0)
+        , fCacheMisses(0)
+#endif
+{
+}
+
+GrD3DResourceProvider::PipelineStateCache::~PipelineStateCache() {
+    // dump stats
+#ifdef GR_PIPELINE_STATE_CACHE_STATS
+    if (c_DisplayMtlPipelineCache) {
+        SkDebugf("--- Pipeline State Cache ---\n");
+        SkDebugf("Total requests: %d\n", fTotalRequests);
+        SkDebugf("Cache misses: %d\n", fCacheMisses);
+        SkDebugf("Cache miss %%: %f\n",
+                 (fTotalRequests > 0) ? 100.f * fCacheMisses / fTotalRequests : 0.f);
+        SkDebugf("---------------------\n");
+    }
+#endif
+}
+
+sk_sp<GrD3DPipelineState> GrD3DResourceProvider::PipelineStateCache::refPipelineState(
+        GrRenderTarget* renderTarget, const GrProgramInfo& programInfo) {
+#ifdef GR_PIPELINE_STATE_CACHE_STATS
+    ++fTotalRequests;
+#endif
+
+    const GrCaps* caps = fGpu->caps();
+
+    GrProgramDesc desc = caps->makeDesc(renderTarget, programInfo);
+    if (!desc.isValid()) {
+        GrCapsDebugf(fGpu->caps(), "Failed to build mtl program descriptor!\n");
+        return nullptr;
+    }
+
+    std::unique_ptr<Entry>* entry = fMap.find(desc);
+    if (!entry) {
+#ifdef GR_PIPELINE_STATE_CACHE_STATS
+        ++fCacheMisses;
+#endif
+        sk_sp<GrD3DPipelineState> pipelineState = GrD3DPipelineStateBuilder::MakePipelineState(
+                fGpu, renderTarget, desc, programInfo);
+        if (!pipelineState) {
+            return nullptr;
+        }
+        entry = fMap.insert(desc, std::unique_ptr<Entry>(
+                new Entry(fGpu, std::move(pipelineState))));
+        return (*entry)->fPipelineState;
+    }
+    return (*entry)->fPipelineState;
+}