| /* | |
| * Copyright 2014 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef GrProgramDesc_DEFINED | |
| #define GrProgramDesc_DEFINED | |
| #include "include/core/SkString.h" | |
| #include "include/private/GrTypesPriv.h" | |
| #include "include/private/SkTArray.h" | |
| #include "include/private/SkTo.h" | |
| #include <limits.h> | |
| class GrCaps; | |
| class GrProgramInfo; | |
| class GrRenderTarget; | |
| class GrShaderCaps; | |
| class GrProcessorKeyBuilder { | |
| public: | |
| GrProcessorKeyBuilder(SkTArray<uint32_t, true>* data) : fData(data) {} | |
| virtual ~GrProcessorKeyBuilder() { | |
| // Ensure that flush was called before we went out of scope | |
| SkASSERT(fBitsUsed == 0); | |
| } | |
| virtual void addBits(uint32_t numBits, uint32_t val, const char* label) { | |
| SkASSERT(numBits > 0 && numBits <= 32); | |
| SkASSERT(numBits == 32 || (val < (1u << numBits))); | |
| fCurValue |= (val << fBitsUsed); | |
| fBitsUsed += numBits; | |
| if (fBitsUsed >= 32) { | |
| // Overflow, start a new working value | |
| fData->push_back(fCurValue); | |
| uint32_t excess = fBitsUsed - 32; | |
| fCurValue = excess ? (val >> (numBits - excess)) : 0; | |
| fBitsUsed = excess; | |
| } | |
| SkASSERT(fCurValue < (1u << fBitsUsed)); | |
| } | |
| void addBytes(uint32_t numBytes, const void* data, const char* label) { | |
| const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data); | |
| for (; numBytes --> 0; bytes++) { | |
| this->addBits(8, *bytes, label); | |
| } | |
| } | |
| void addBool(bool b, const char* label) { | |
| this->addBits(1, b, label); | |
| } | |
| void add32(uint32_t v, const char* label = "unknown") { | |
| this->addBits(32, v, label); | |
| } | |
| virtual void appendComment(const char* comment) {} | |
| // Introduces a word-boundary in the key. Must be called before using the key with any cache, | |
| // but can also be called to create a break between generic data and backend-specific data. | |
| void flush() { | |
| if (fBitsUsed) { | |
| fData->push_back(fCurValue); | |
| fCurValue = 0; | |
| fBitsUsed = 0; | |
| } | |
| } | |
| private: | |
| SkTArray<uint32_t, true>* fData; | |
| uint32_t fCurValue = 0; | |
| uint32_t fBitsUsed = 0; // ... in current value | |
| }; | |
| class GrProcessorStringKeyBuilder : public GrProcessorKeyBuilder { | |
| public: | |
| GrProcessorStringKeyBuilder(SkTArray<uint32_t, true>* data) : INHERITED(data) {} | |
| void addBits(uint32_t numBits, uint32_t val, const char* label) override { | |
| INHERITED::addBits(numBits, val, label); | |
| fDescription.appendf("%s: %u\n", label, val); | |
| } | |
| void appendComment(const char* comment) override { | |
| fDescription.appendf("%s\n", comment); | |
| } | |
| SkString description() const { return fDescription; } | |
| private: | |
| using INHERITED = GrProcessorKeyBuilder; | |
| SkString fDescription; | |
| }; | |
| /** This class is used to generate a generic program cache key. The Dawn, Metal and Vulkan | |
| * backends derive backend-specific versions which add additional information. | |
| */ | |
| class GrProgramDesc { | |
| public: | |
| GrProgramDesc(const GrProgramDesc& other) = default; | |
| GrProgramDesc& operator=(const GrProgramDesc &other) = default; | |
| bool isValid() const { return !fKey.empty(); } | |
| void reset() { *this = GrProgramDesc{}; } | |
| // Returns this as a uint32_t array to be used as a key in the program cache. | |
| const uint32_t* asKey() const { | |
| return fKey.data(); | |
| } | |
| // Gets the number of bytes in asKey(). It will be a 4-byte aligned value. | |
| uint32_t keyLength() const { | |
| return fKey.size() * sizeof(uint32_t); | |
| } | |
| bool operator== (const GrProgramDesc& that) const { | |
| return this->fKey == that.fKey; | |
| } | |
| bool operator!= (const GrProgramDesc& other) const { | |
| return !(*this == other); | |
| } | |
| uint32_t initialKeyLength() const { return fInitialKeyLength; } | |
| // TODO(skia:11372): Incorporate this into caps interface (part of makeDesc, or a parallel | |
| // function), so other backends can include their information in the description. | |
| static SkString Describe(const GrProgramInfo&, const GrCaps&); | |
| protected: | |
| friend class GrDawnCaps; | |
| friend class GrD3DCaps; | |
| friend class GrGLCaps; | |
| friend class GrMockCaps; | |
| friend class GrMtlCaps; | |
| friend class GrVkCaps; | |
| friend class GrGLGpu; // for ProgramCache to access BuildFromData | |
| friend class GrMtlResourceProvider; // for PipelineStateCache to access BuildFromData | |
| // Creates an uninitialized key that must be populated by Build | |
| GrProgramDesc() {} | |
| /** | |
| * Builds a program descriptor. | |
| * | |
| * @param desc The built descriptor | |
| * @param programInfo Program information need to build the key | |
| * @param caps the caps | |
| **/ | |
| static void Build(GrProgramDesc*, const GrProgramInfo&, const GrCaps&); | |
| // This is strictly an OpenGL call since the other backends have additional data in their keys. | |
| static bool BuildFromData(GrProgramDesc* desc, const void* keyData, size_t keyLength) { | |
| if (!SkTFitsIn<int>(keyLength) || !SkIsAlign4(keyLength)) { | |
| return false; | |
| } | |
| desc->fKey.reset(keyLength / 4); | |
| memcpy(desc->fKey.begin(), keyData, keyLength); | |
| return true; | |
| } | |
| enum { | |
| kHeaderSize = 1, // "header" in ::Build | |
| kMaxPreallocProcessors = 8, | |
| kIntsPerProcessor = 4, // This is an overestimate of the average effect key size. | |
| kPreAllocSize = kHeaderSize + | |
| kMaxPreallocProcessors * kIntsPerProcessor, | |
| }; | |
| using KeyType = SkSTArray<kPreAllocSize, uint32_t, true>; | |
| KeyType* key() { return &fKey; } | |
| private: | |
| SkSTArray<kPreAllocSize, uint32_t, true> fKey; | |
| uint32_t fInitialKeyLength = 0; | |
| }; | |
| #endif |