1 /*
2  * Copyright 2014 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 #ifndef GrProgramDesc_DEFINED
9 #define GrProgramDesc_DEFINED
10 
11 #include "include/core/SkString.h"
12 #include "include/private/GrTypesPriv.h"
13 #include "include/private/SkTArray.h"
14 #include "include/private/SkTo.h"
15 
16 #include <limits.h>
17 
18 class GrCaps;
19 class GrProgramInfo;
20 class GrRenderTarget;
21 class GrShaderCaps;
22 
23 class GrProcessorKeyBuilder {
24 public:
GrProcessorKeyBuilder(SkTArray<uint32_t,true> * data)25     GrProcessorKeyBuilder(SkTArray<uint32_t, true>* data) : fData(data) {}
26 
~GrProcessorKeyBuilder()27     virtual ~GrProcessorKeyBuilder() {
28         // Ensure that flush was called before we went out of scope
29         SkASSERT(fBitsUsed == 0);
30     }
31 
addBits(uint32_t numBits,uint32_t val,const char * label)32     virtual void addBits(uint32_t numBits, uint32_t val, const char* label) {
33         SkASSERT(numBits > 0 && numBits <= 32);
34         SkASSERT(numBits == 32 || (val < (1u << numBits)));
35 
36         fCurValue |= (val << fBitsUsed);
37         fBitsUsed += numBits;
38 
39         if (fBitsUsed >= 32) {
40             // Overflow, start a new working value
41             fData->push_back(fCurValue);
42             uint32_t excess = fBitsUsed - 32;
43             fCurValue = excess ? (val >> (numBits - excess)) : 0;
44             fBitsUsed = excess;
45         }
46 
47         SkASSERT(fCurValue < (1u << fBitsUsed));
48     }
49 
addBytes(uint32_t numBytes,const void * data,const char * label)50     void addBytes(uint32_t numBytes, const void* data, const char* label) {
51         const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
52         for (; numBytes --> 0; bytes++) {
53             this->addBits(8, *bytes, label);
54         }
55     }
56 
addBool(bool b,const char * label)57     void addBool(bool b, const char* label) {
58         this->addBits(1, b, label);
59     }
60 
61     void add32(uint32_t v, const char* label = "unknown") {
62         this->addBits(32, v, label);
63     }
64 
appendComment(const char * comment)65     virtual void appendComment(const char* comment) {}
66 
67     // Introduces a word-boundary in the key. Must be called before using the key with any cache,
68     // but can also be called to create a break between generic data and backend-specific data.
flush()69     void flush() {
70         if (fBitsUsed) {
71             fData->push_back(fCurValue);
72             fCurValue = 0;
73             fBitsUsed = 0;
74         }
75     }
76 
77 private:
78     SkTArray<uint32_t, true>* fData;
79     uint32_t fCurValue = 0;
80     uint32_t fBitsUsed = 0;  // ... in current value
81 };
82 
83 class GrProcessorStringKeyBuilder : public GrProcessorKeyBuilder {
84 public:
GrProcessorStringKeyBuilder(SkTArray<uint32_t,true> * data)85     GrProcessorStringKeyBuilder(SkTArray<uint32_t, true>* data) : INHERITED(data) {}
86 
addBits(uint32_t numBits,uint32_t val,const char * label)87     void addBits(uint32_t numBits, uint32_t val, const char* label) override {
88         INHERITED::addBits(numBits, val, label);
89         fDescription.appendf("%s: %u\n", label, val);
90     }
91 
appendComment(const char * comment)92     void appendComment(const char* comment) override {
93         fDescription.appendf("%s\n", comment);
94     }
95 
description()96     SkString description() const { return fDescription; }
97 
98 private:
99     using INHERITED = GrProcessorKeyBuilder;
100     SkString fDescription;
101 };
102 
103 /** This class is used to generate a generic program cache key. The Dawn, Metal and Vulkan
104  *  backends derive backend-specific versions which add additional information.
105  */
106 class GrProgramDesc {
107 public:
108     GrProgramDesc(const GrProgramDesc& other) = default;
109 
isValid()110     bool isValid() const { return !fKey.empty(); }
reset()111     void reset() { *this = GrProgramDesc{}; }
112 
113     // Returns this as a uint32_t array to be used as a key in the program cache.
asKey()114     const uint32_t* asKey() const {
115         return fKey.data();
116     }
117 
118     // Gets the number of bytes in asKey(). It will be a 4-byte aligned value.
keyLength()119     uint32_t keyLength() const {
120         return fKey.size() * sizeof(uint32_t);
121     }
122 
123     bool operator== (const GrProgramDesc& that) const {
124         return this->fKey == that.fKey;
125     }
126 
127     bool operator!= (const GrProgramDesc& other) const {
128         return !(*this == other);
129     }
130 
initialKeyLength()131     uint32_t initialKeyLength() const { return fInitialKeyLength; }
132 
133     // TODO(skia:11372): Incorporate this into caps interface (part of makeDesc, or a parallel
134     // function), so other backends can include their information in the description.
135     static SkString Describe(const GrProgramInfo&, const GrCaps&);
136 
137 protected:
138     friend class GrDawnCaps;
139     friend class GrD3DCaps;
140     friend class GrGLCaps;
141     friend class GrMockCaps;
142     friend class GrMtlCaps;
143     friend class GrVkCaps;
144 
145     friend class GrGLGpu; // for ProgramCache to access BuildFromData
146     friend class GrMtlResourceProvider; // for PipelineStateCache to access BuildFromData
147 
148     // Creates an uninitialized key that must be populated by Build
GrProgramDesc()149     GrProgramDesc() {}
150 
151     /**
152      * Builds a program descriptor.
153      *
154      * @param desc          The built descriptor
155      * @param programInfo   Program information need to build the key
156      * @param caps          the caps
157      **/
158     static void Build(GrProgramDesc*, const GrProgramInfo&, const GrCaps&);
159 
160     // This is strictly an OpenGL call since the other backends have additional data in their keys.
BuildFromData(GrProgramDesc * desc,const void * keyData,size_t keyLength)161     static bool BuildFromData(GrProgramDesc* desc, const void* keyData, size_t keyLength) {
162         if (!SkTFitsIn<int>(keyLength) || !SkIsAlign4(keyLength)) {
163             return false;
164         }
165         desc->fKey.reset(keyLength / 4);
166         memcpy(desc->fKey.begin(), keyData, keyLength);
167         return true;
168     }
169 
170     enum {
171         kHeaderSize            = 1,    // "header" in ::Build
172         kMaxPreallocProcessors = 8,
173         kIntsPerProcessor      = 4,    // This is an overestimate of the average effect key size.
174         kPreAllocSize = kHeaderSize +
175                         kMaxPreallocProcessors * kIntsPerProcessor,
176     };
177 
178     using KeyType = SkSTArray<kPreAllocSize, uint32_t, true>;
179 
key()180     KeyType* key() { return &fKey; }
181 
182 private:
183     SkSTArray<kPreAllocSize, uint32_t, true> fKey;
184     uint32_t fInitialKeyLength = 0;
185 };
186 
187 #endif
188