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 GrPathRange_DEFINED
9 #define GrPathRange_DEFINED
10 
11 #include "GrGpuResource.h"
12 #include "SkPath.h"
13 #include "SkRefCnt.h"
14 #include "SkTArray.h"
15 
16 class SkDescriptor;
17 
18 /**
19  * Represents a contiguous range of GPU path objects.
20  * This object is immutable with the exception that individual paths may be
21  * initialized lazily.
22  */
23 
24 class GrPathRange : public GrGpuResource {
25 public:
26 
27 
28     enum PathIndexType {
29         kU8_PathIndexType,   //!< uint8_t
30         kU16_PathIndexType,  //!< uint16_t
31         kU32_PathIndexType,  //!< uint32_t
32 
33         kLast_PathIndexType = kU32_PathIndexType
34     };
35 
PathIndexSizeInBytes(PathIndexType type)36     static inline int PathIndexSizeInBytes(PathIndexType type) {
37         GR_STATIC_ASSERT(0 == kU8_PathIndexType);
38         GR_STATIC_ASSERT(1 == kU16_PathIndexType);
39         GR_STATIC_ASSERT(2 == kU32_PathIndexType);
40         GR_STATIC_ASSERT(kU32_PathIndexType == kLast_PathIndexType);
41 
42         return 1 << type;
43     }
44 
45     /**
46      * Class that generates the paths for a specific range.
47      */
48     class PathGenerator : public SkRefCnt {
49     public:
50         virtual int getNumPaths() = 0;
51         virtual void generatePath(int index, SkPath* out) = 0;
52 #ifdef SK_DEBUG
isEqualTo(const SkDescriptor &)53         virtual bool isEqualTo(const SkDescriptor&) const { return false; }
54 #endif
~PathGenerator()55         virtual ~PathGenerator() {}
56     };
57 
58     /**
59      * Initialize a lazy-loaded path range. This class will generate an SkPath and call
60      * onInitPath() for each path within the range before it is drawn for the first time.
61      */
62     GrPathRange(GrGpu*, PathGenerator*);
63 
64     /**
65      * Initialize an eager-loaded path range. The subclass is responsible for ensuring all
66      * the paths are initialized up front.
67      */
68     GrPathRange(GrGpu*, int numPaths);
69 
getNumPaths()70     int getNumPaths() const { return fNumPaths; }
getPathGenerator()71     const PathGenerator* getPathGenerator() const { return fPathGenerator.get(); }
72 
73     void loadPathsIfNeeded(const void* indices, PathIndexType, int count) const;
74 
loadPathsIfNeeded(const IndexType * indices,int count)75     template<typename IndexType> void loadPathsIfNeeded(const IndexType* indices, int count) const {
76         if (!fPathGenerator) {
77             return;
78         }
79 
80         bool didLoadPaths = false;
81 
82         for (int i = 0; i < count; ++i) {
83             SkASSERT(indices[i] < static_cast<uint32_t>(fNumPaths));
84 
85             const int groupIndex = indices[i] / kPathsPerGroup;
86             const int groupByte = groupIndex / 8;
87             const uint8_t groupBit = 1 << (groupIndex % 8);
88 
89             const bool hasPath = SkToBool(fGeneratedPaths[groupByte] & groupBit);
90             if (!hasPath) {
91                 // We track which paths are loaded in groups of kPathsPerGroup. To
92                 // mark a path as loaded we need to load the entire group.
93                 const int groupFirstPath = groupIndex * kPathsPerGroup;
94                 const int groupLastPath = SkTMin(groupFirstPath + kPathsPerGroup, fNumPaths) - 1;
95 
96                 SkPath path;
97                 for (int pathIdx = groupFirstPath; pathIdx <= groupLastPath; ++pathIdx) {
98                     fPathGenerator->generatePath(pathIdx, &path);
99                     this->onInitPath(pathIdx, path);
100                 }
101 
102                 fGeneratedPaths[groupByte] |= groupBit;
103                 didLoadPaths = true;
104             }
105         }
106 
107         if (didLoadPaths) {
108             this->didChangeGpuMemorySize();
109         }
110     }
111 
112 #ifdef SK_DEBUG
113     void assertPathsLoaded(const void* indices, PathIndexType, int count) const;
114 
assertPathsLoaded(const IndexType * indices,int count)115     template<typename IndexType> void assertPathsLoaded(const IndexType* indices, int count) const {
116         if (!fPathGenerator) {
117             return;
118         }
119 
120         for (int i = 0; i < count; ++i) {
121             SkASSERT(indices[i] < static_cast<uint32_t>(fNumPaths));
122 
123             const int groupIndex = indices[i] / kPathsPerGroup;
124             const int groupByte = groupIndex / 8;
125             const uint8_t groupBit = 1 << (groupIndex % 8);
126 
127             SkASSERT(fGeneratedPaths[groupByte] & groupBit);
128         }
129     }
130 
isEqualTo(const SkDescriptor & desc)131     virtual bool isEqualTo(const SkDescriptor& desc) const {
132         return nullptr != fPathGenerator.get() && fPathGenerator->isEqualTo(desc);
133     }
134 #endif
135 protected:
136     // Initialize a path in the range before drawing. This is only called when
137     // fPathGenerator is non-null. The child class need not call didChangeGpuMemorySize(),
138     // GrPathRange will take care of that after the call is complete.
139     virtual void onInitPath(int index, const SkPath&) const = 0;
140 
141 private:
142     enum {
143         kPathsPerGroup = 16 // Paths get tracked in groups of 16 for lazy loading.
144     };
145 
146     mutable SkAutoTUnref<PathGenerator> fPathGenerator;
147     mutable SkTArray<uint8_t, true /*MEM_COPY*/> fGeneratedPaths;
148     const int fNumPaths;
149 
150     typedef GrGpuResource INHERITED;
151 };
152 
153 #endif
154