1/*
2 * Copyright 2016 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 "GrVkResourceProvider.h"
9
10#include "GrVkGpu.h"
11#include "GrProcessor.h"
12#include "GrRenderTargetPriv.h" // TODO: remove once refPipelineState gets passed stencil settings.
13#include "GrVkPipelineState.h"
14#include "GrVkPipelineStateBuilder.h"
15#include "SkOpts.h"
16#include "glsl/GrGLSLFragmentProcessor.h"
17#include "glsl/GrGLSLProgramDataManager.h"
18
19#ifdef GR_PIPELINE_STATE_CACHE_STATS
20// Display pipeline state cache usage
21static const bool c_DisplayVkPipelineCache{false};
22#endif
23
24struct GrVkResourceProvider::PipelineStateCache::Entry {
25    Entry(GrVkGpu* gpu, sk_sp<GrVkPipelineState> pipelineState)
26    : fGpu(gpu)
27    , fPipelineState(pipelineState) {}
28
29    ~Entry() {
30        if (fPipelineState) {
31            fPipelineState->freeGPUResources(fGpu);
32        }
33    }
34
35    GrVkGpu* fGpu;
36    sk_sp<GrVkPipelineState> fPipelineState;
37};
38
39GrVkResourceProvider::PipelineStateCache::PipelineStateCache(GrVkGpu* gpu)
40    : fMap(kMaxEntries)
41    , fGpu(gpu)
42#ifdef GR_PIPELINE_STATE_CACHE_STATS
43    , fTotalRequests(0)
44    , fCacheMisses(0)
45#endif
46{}
47
48GrVkResourceProvider::PipelineStateCache::~PipelineStateCache() {
49    SkASSERT(0 == fMap.count());
50    // dump stats
51#ifdef GR_PIPELINE_STATE_CACHE_STATS
52    if (c_DisplayVkPipelineCache) {
53        SkDebugf("--- Pipeline State Cache ---\n");
54        SkDebugf("Total requests: %d\n", fTotalRequests);
55        SkDebugf("Cache misses: %d\n", fCacheMisses);
56        SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ?
57                 100.f * fCacheMisses / fTotalRequests :
58                 0.f);
59        SkDebugf("---------------------\n");
60    }
61#endif
62}
63
64void GrVkResourceProvider::PipelineStateCache::abandon() {
65    fMap.foreach([](std::unique_ptr<Entry>* e) {
66        (*e)->fPipelineState->abandonGPUResources();
67        (*e)->fPipelineState = nullptr;
68    });
69    fMap.reset();
70}
71
72void GrVkResourceProvider::PipelineStateCache::release() {
73    fMap.reset();
74}
75
76sk_sp<GrVkPipelineState> GrVkResourceProvider::PipelineStateCache::refPipelineState(
77                                                               const GrPipeline& pipeline,
78                                                               const GrPrimitiveProcessor& primProc,
79                                                               GrPrimitiveType primitiveType,
80                                                               const GrVkRenderPass& renderPass) {
81#ifdef GR_PIPELINE_STATE_CACHE_STATS
82    ++fTotalRequests;
83#endif
84    GrStencilSettings stencil;
85    if (pipeline.isStencilEnabled()) {
86        GrRenderTarget* rt = pipeline.getRenderTarget();
87        // TODO: attach stencil and create settings during render target flush.
88        SkASSERT(rt->renderTargetPriv().getStencilAttachment());
89        stencil.reset(*pipeline.getUserStencil(), pipeline.hasStencilClip(),
90                      rt->renderTargetPriv().numStencilBits());
91    }
92
93    // Get GrVkProgramDesc
94    GrVkPipelineState::Desc desc;
95    if (!GrVkPipelineState::Desc::Build(&desc, primProc, pipeline, stencil,
96                                        primitiveType, *fGpu->caps()->shaderCaps())) {
97        GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n");
98        return nullptr;
99    }
100    desc.finalize();
101
102    std::unique_ptr<Entry>* entry = fMap.find(desc);
103    if (!entry) {
104        // Didn't find an origin-independent version, check with the specific origin
105        GrSurfaceOrigin origin = pipeline.getRenderTarget()->origin();
106        desc.setSurfaceOriginKey(GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(origin));
107        desc.finalize();
108        entry = fMap.find(desc);
109    }
110    if (!entry) {
111#ifdef GR_PIPELINE_STATE_CACHE_STATS
112        ++fCacheMisses;
113#endif
114        sk_sp<GrVkPipelineState> pipelineState(
115            GrVkPipelineStateBuilder::CreatePipelineState(fGpu,
116                                                          pipeline,
117                                                          stencil,
118                                                          primProc,
119                                                          primitiveType,
120                                                          &desc,
121                                                          renderPass));
122        if (nullptr == pipelineState) {
123            return nullptr;
124        }
125        entry = fMap.insert(desc, std::unique_ptr<Entry>(new Entry(fGpu,
126                                                                   std::move(pipelineState))));
127        return (*entry)->fPipelineState;
128    }
129    return (*entry)->fPipelineState;
130}
131