1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrMtlResourceProvider.h"
9
10#include "GrMtlCopyManager.h"
11#include "GrMtlGpu.h"
12#include "GrMtlPipelineState.h"
13#include "GrMtlUtil.h"
14
15#include "SkSLCompiler.h"
16
17
18GrMtlResourceProvider::GrMtlResourceProvider(GrMtlGpu* gpu)
19    : fGpu(gpu) {
20    fPipelineStateCache.reset(new PipelineStateCache(gpu));
21}
22
23GrMtlCopyPipelineState* GrMtlResourceProvider::findOrCreateCopyPipelineState(
24        MTLPixelFormat dstPixelFormat,
25        id<MTLFunction> vertexFunction,
26        id<MTLFunction> fragmentFunction,
27        MTLVertexDescriptor* vertexDescriptor) {
28
29    for (const auto& copyPipelineState: fCopyPipelineStateCache) {
30        if (GrMtlCopyManager::IsCompatible(copyPipelineState.get(), dstPixelFormat)) {
31            return copyPipelineState.get();
32        }
33    }
34
35    fCopyPipelineStateCache.emplace_back(GrMtlCopyPipelineState::CreateCopyPipelineState(
36             fGpu, dstPixelFormat, vertexFunction, fragmentFunction, vertexDescriptor));
37    return fCopyPipelineStateCache.back().get();
38}
39
40GrMtlPipelineState* GrMtlResourceProvider::findOrCreateCompatiblePipelineState(
41        GrRenderTarget* renderTarget, GrSurfaceOrigin origin,
42        const GrPipeline& pipeline, const GrPrimitiveProcessor& proc,
43        const GrTextureProxy* const primProcProxies[], GrPrimitiveType primType) {
44    return fPipelineStateCache->refPipelineState(renderTarget, origin, proc, primProcProxies,
45                                                 pipeline, primType);
46}
47
48////////////////////////////////////////////////////////////////////////////////////////////////
49
50#ifdef GR_PIPELINE_STATE_CACHE_STATS
51// Display pipeline state cache usage
52static const bool c_DisplayMtlPipelineCache{false};
53#endif
54
55struct GrMtlResourceProvider::PipelineStateCache::Entry {
56    Entry(GrMtlGpu* gpu, GrMtlPipelineState* pipelineState)
57    : fGpu(gpu)
58    , fPipelineState(pipelineState) {}
59
60    GrMtlGpu* fGpu;
61    std::unique_ptr<GrMtlPipelineState> fPipelineState;
62};
63
64GrMtlResourceProvider::PipelineStateCache::PipelineStateCache(GrMtlGpu* gpu)
65    : fMap(kMaxEntries)
66    , fGpu(gpu)
67#ifdef GR_PIPELINE_STATE_CACHE_STATS
68    , fTotalRequests(0)
69    , fCacheMisses(0)
70#endif
71{}
72
73GrMtlResourceProvider::PipelineStateCache::~PipelineStateCache() {
74    // TODO: determine if we need abandon/release methods
75    fMap.reset();
76    // dump stats
77#ifdef GR_PIPELINE_STATE_CACHE_STATS
78    if (c_DisplayMtlPipelineCache) {
79        SkDebugf("--- Pipeline State Cache ---\n");
80        SkDebugf("Total requests: %d\n", fTotalRequests);
81        SkDebugf("Cache misses: %d\n", fCacheMisses);
82        SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ?
83                 100.f * fCacheMisses / fTotalRequests :
84                 0.f);
85        SkDebugf("---------------------\n");
86    }
87#endif
88}
89
90GrMtlPipelineState* GrMtlResourceProvider::PipelineStateCache::refPipelineState(
91        GrRenderTarget* renderTarget,
92        GrSurfaceOrigin origin,
93        const GrPrimitiveProcessor& primProc,
94        const GrTextureProxy* const primProcProxies[],
95        const GrPipeline& pipeline,
96        GrPrimitiveType primType) {
97#ifdef GR_PIPELINE_STATE_CACHE_STATS
98    ++fTotalRequests;
99#endif
100    // Get GrMtlProgramDesc
101    GrMtlPipelineStateBuilder::Desc desc;
102    if (!GrMtlPipelineStateBuilder::Desc::Build(&desc, renderTarget, primProc, pipeline, primType,
103                                                fGpu)) {
104        GrCapsDebugf(fGpu->caps(), "Failed to build mtl program descriptor!\n");
105        return nullptr;
106    }
107
108    std::unique_ptr<Entry>* entry = fMap.find(desc);
109    if (!entry) {
110        // Didn't find an origin-independent version, check with the specific origin
111        desc.setSurfaceOriginKey(GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(origin));
112        entry = fMap.find(desc);
113    }
114    if (!entry) {
115#ifdef GR_PIPELINE_STATE_CACHE_STATS
116        ++fCacheMisses;
117#endif
118        GrMtlPipelineState* pipelineState(GrMtlPipelineStateBuilder::CreatePipelineState(
119                fGpu, renderTarget, origin, primProc, primProcProxies, pipeline, &desc));
120        if (nullptr == pipelineState) {
121            return nullptr;
122        }
123        entry = fMap.insert(desc, std::unique_ptr<Entry>(new Entry(fGpu, pipelineState)));
124        return (*entry)->fPipelineState.get();
125    }
126    return (*entry)->fPipelineState.get();
127}
128