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 "GrColor.h"
12#include "GrTypesPriv.h"
13#include "SkChecksum.h"
14
15/** This class describes a program to generate. It also serves as a program cache key. Very little
16    of this is GL-specific. The GL-specific parts could be factored out into a subclass. */
17class GrProgramDesc {
18public:
19    // Creates an uninitialized key that must be populated by GrGpu::buildProgramDesc()
20    GrProgramDesc() {}
21
22    // Returns this as a uint32_t array to be used as a key in the program cache.
23    const uint32_t* asKey() const {
24        return reinterpret_cast<const uint32_t*>(fKey.begin());
25    }
26
27    // Gets the number of bytes in asKey(). It will be a 4-byte aligned value. When comparing two
28    // keys the size of either key can be used with memcmp() since the lengths themselves begin the
29    // keys and thus the memcmp will exit early if the keys are of different lengths.
30    uint32_t keyLength() const { return *this->atOffset<uint32_t, kLengthOffset>(); }
31
32    // Gets the a checksum of the key. Can be used as a hash value for a fast lookup in a cache.
33    uint32_t getChecksum() const { return *this->atOffset<uint32_t, kChecksumOffset>(); }
34
35    GrProgramDesc& operator= (const GrProgramDesc& other) {
36        uint32_t keyLength = other.keyLength();
37        fKey.reset(SkToInt(keyLength));
38        memcpy(fKey.begin(), other.fKey.begin(), keyLength);
39        return *this;
40    }
41
42    bool operator== (const GrProgramDesc& that) const {
43        SkASSERT(SkIsAlign4(this->keyLength()));
44        int l = this->keyLength() >> 2;
45        const uint32_t* aKey = this->asKey();
46        const uint32_t* bKey = that.asKey();
47        for (int i = 0; i < l; ++i) {
48            if (aKey[i] != bKey[i]) {
49                return false;
50            }
51        }
52        return true;
53    }
54
55    bool operator!= (const GrProgramDesc& other) const {
56        return !(*this == other);
57    }
58
59    static bool Less(const GrProgramDesc& a, const GrProgramDesc& b) {
60        SkASSERT(SkIsAlign4(a.keyLength()));
61        int l = a.keyLength() >> 2;
62        const uint32_t* aKey = a.asKey();
63        const uint32_t* bKey = b.asKey();
64        for (int i = 0; i < l; ++i) {
65            if (aKey[i] != bKey[i]) {
66                return aKey[i] < bKey[i] ? true : false;
67            }
68        }
69        return false;
70    }
71
72    struct KeyHeader {
73        // Set by GrGLShaderBuilder if there are effects that read the fragment position. Otherwise,
74        // 0.
75        uint8_t                     fFragPosKey;
76        // Set to uniquely idenitify any swizzling of the shader's output color(s).
77        uint8_t                     fOutputSwizzle;
78        uint8_t                     fSnapVerticesToPixelCenters;
79        int8_t                      fColorEffectCnt;
80        int8_t                      fCoverageEffectCnt;
81        uint8_t                     fIgnoresCoverage;
82    };
83
84    int numColorEffects() const {
85        return this->header().fColorEffectCnt;
86    }
87
88    int numCoverageEffects() const {
89        return this->header().fCoverageEffectCnt;
90    }
91
92    int numTotalEffects() const { return this->numColorEffects() + this->numCoverageEffects(); }
93
94    // This should really only be used internally, base classes should return their own headers
95    const KeyHeader& header() const { return *this->atOffset<KeyHeader, kHeaderOffset>(); }
96
97protected:
98    template<typename T, size_t OFFSET> T* atOffset() {
99        return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(fKey.begin()) + OFFSET);
100    }
101
102    template<typename T, size_t OFFSET> const T* atOffset() const {
103        return reinterpret_cast<const T*>(reinterpret_cast<intptr_t>(fKey.begin()) + OFFSET);
104    }
105
106    void finalize() {
107        int keyLength = fKey.count();
108        SkASSERT(0 == (keyLength % 4));
109        *(this->atOffset<uint32_t, GrProgramDesc::kLengthOffset>()) = SkToU32(keyLength);
110
111        uint32_t* checksum = this->atOffset<uint32_t, GrProgramDesc::kChecksumOffset>();
112        *checksum = 0;  // We'll hash through these bytes, so make sure they're initialized.
113        *checksum = SkChecksum::Murmur3(fKey.begin(), keyLength);
114    }
115
116    // The key, stored in fKey, is composed of four parts:
117    // 1. uint32_t for total key length.
118    // 2. uint32_t for a checksum.
119    // 3. Header struct defined above.  Also room for extensions to the header
120    // 4. A Backend specific payload.  Room is preallocated for this
121    enum KeyOffsets {
122        // Part 1.
123        kLengthOffset = 0,
124        // Part 2.
125        kChecksumOffset = kLengthOffset + sizeof(uint32_t),
126        // Part 3.
127        kHeaderOffset = kChecksumOffset + sizeof(uint32_t),
128        kHeaderSize = SkAlign4(2 * sizeof(KeyHeader)),
129    };
130
131    enum {
132        kMaxPreallocProcessors = 8,
133        kIntsPerProcessor      = 4,    // This is an overestimate of the average effect key size.
134        kPreAllocSize = kHeaderOffset + kHeaderSize +
135                        kMaxPreallocProcessors * sizeof(uint32_t) * kIntsPerProcessor,
136    };
137
138    SkSTArray<kPreAllocSize, uint8_t, true>& key() { return fKey; }
139    const SkSTArray<kPreAllocSize, uint8_t, true>& key() const { return fKey; }
140
141private:
142    SkSTArray<kPreAllocSize, uint8_t, true> fKey;
143};
144
145#endif
146