1/*
2 * Copyright 2011 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 <memory>
9
10#include "src/gpu/gl/GrGLGpu.h"
11
12#include "include/gpu/GrContextOptions.h"
13#include "include/gpu/GrDirectContext.h"
14#include "src/gpu/GrDirectContextPriv.h"
15#include "src/gpu/GrFragmentProcessor.h"
16#include "src/gpu/GrProcessor.h"
17#include "src/gpu/GrProgramDesc.h"
18#include "src/gpu/gl/builders/GrGLProgramBuilder.h"
19
20struct GrGLGpu::ProgramCache::Entry {
21    Entry(sk_sp<GrGLProgram> program)
22        : fProgram(std::move(program)) {}
23
24    Entry(const GrGLPrecompiledProgram& precompiledProgram)
25        : fPrecompiledProgram(precompiledProgram) {}
26
27    sk_sp<GrGLProgram> fProgram;
28    GrGLPrecompiledProgram fPrecompiledProgram;
29};
30
31GrGLGpu::ProgramCache::ProgramCache(int runtimeProgramCacheSize)
32    : fMap(runtimeProgramCacheSize) {
33}
34
35GrGLGpu::ProgramCache::~ProgramCache() {}
36
37void GrGLGpu::ProgramCache::abandon() {
38    fMap.foreach([](GrProgramDesc*, std::unique_ptr<Entry>* e) {
39        if ((*e)->fProgram) {
40            (*e)->fProgram->abandon();
41        }
42    });
43
44    this->reset();
45}
46
47void GrGLGpu::ProgramCache::reset() {
48    fMap.reset();
49}
50
51sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgram(GrDirectContext* dContext,
52                                                              const GrProgramInfo& programInfo) {
53    const GrCaps* caps = dContext->priv().caps();
54
55    GrProgramDesc desc = caps->makeDesc(/*renderTarget*/nullptr, programInfo);
56    if (!desc.isValid()) {
57        GrCapsDebugf(caps, "Failed to gl program descriptor!\n");
58        return nullptr;
59    }
60
61    Stats::ProgramCacheResult stat;
62    sk_sp<GrGLProgram> tmp = this->findOrCreateProgramImpl(dContext, desc, programInfo, &stat);
63    if (!tmp) {
64        fStats.incNumInlineCompilationFailures();
65    } else {
66        fStats.incNumInlineProgramCacheResult(stat);
67    }
68
69    return tmp;
70}
71
72sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgram(GrDirectContext* dContext,
73                                                              const GrProgramDesc& desc,
74                                                              const GrProgramInfo& programInfo,
75                                                              Stats::ProgramCacheResult* stat) {
76    sk_sp<GrGLProgram> tmp = this->findOrCreateProgramImpl(dContext, desc, programInfo, stat);
77    if (!tmp) {
78        fStats.incNumPreCompilationFailures();
79    } else {
80        fStats.incNumPreProgramCacheResult(*stat);
81    }
82
83    return tmp;
84}
85
86sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgramImpl(GrDirectContext* dContext,
87                                                                  const GrProgramDesc& desc,
88                                                                  const GrProgramInfo& programInfo,
89                                                                  Stats::ProgramCacheResult* stat) {
90    *stat = Stats::ProgramCacheResult::kHit;
91    std::unique_ptr<Entry>* entry = fMap.find(desc);
92    if (entry && !(*entry)->fProgram) {
93        // We've pre-compiled the GL program, but don't have the GrGLProgram scaffolding
94        const GrGLPrecompiledProgram* precompiledProgram = &((*entry)->fPrecompiledProgram);
95        SkASSERT(precompiledProgram->fProgramID != 0);
96        (*entry)->fProgram = GrGLProgramBuilder::CreateProgram(dContext, desc, programInfo,
97                                                               precompiledProgram);
98        if (!(*entry)->fProgram) {
99            // Should we purge the program ID from the cache at this point?
100            SkDEBUGFAIL("Couldn't create program from precompiled program");
101            fStats.incNumCompilationFailures();
102            return nullptr;
103        }
104        fStats.incNumPartialCompilationSuccesses();
105        *stat = Stats::ProgramCacheResult::kPartial;
106    } else if (!entry) {
107        // We have a cache miss
108        sk_sp<GrGLProgram> program = GrGLProgramBuilder::CreateProgram(dContext, desc, programInfo);
109        if (!program) {
110            fStats.incNumCompilationFailures();
111            return nullptr;
112        }
113        fStats.incNumCompilationSuccesses();
114        entry = fMap.insert(desc, std::make_unique<Entry>(std::move(program)));
115        *stat = Stats::ProgramCacheResult::kMiss;
116    }
117
118    return (*entry)->fProgram;
119}
120
121bool GrGLGpu::ProgramCache::precompileShader(GrDirectContext* dContext,
122                                             const SkData& key,
123                                             const SkData& data) {
124    GrProgramDesc desc;
125    if (!GrProgramDesc::BuildFromData(&desc, key.data(), key.size())) {
126        return false;
127    }
128
129    std::unique_ptr<Entry>* entry = fMap.find(desc);
130    if (entry) {
131        // We've already seen/compiled this shader
132        return true;
133    }
134
135    GrGLPrecompiledProgram precompiledProgram;
136    if (!GrGLProgramBuilder::PrecompileProgram(dContext, &precompiledProgram, data)) {
137        return false;
138    }
139
140    fMap.insert(desc, std::make_unique<Entry>(precompiledProgram));
141    return true;
142}
143