aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrPathRange.h
blob: 7bca17f26057a30ed2ba5a0abe33e8fee5c653c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * 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 GrPathRange_DEFINED
#define GrPathRange_DEFINED

#include "GrGpuResource.h"
#include "SkPath.h"
#include "SkRefCnt.h"
#include "SkTArray.h"

class SkDescriptor;

/**
 * Represents a contiguous range of GPU path objects.
 * This object is immutable with the exception that individual paths may be
 * initialized lazily.
 */

class GrPathRange : public GrGpuResource {
public:


    enum PathIndexType {
        kU8_PathIndexType,   //!< uint8_t
        kU16_PathIndexType,  //!< uint16_t
        kU32_PathIndexType,  //!< uint32_t

        kLast_PathIndexType = kU32_PathIndexType
    };

    static inline int PathIndexSizeInBytes(PathIndexType type) {
        GR_STATIC_ASSERT(0 == kU8_PathIndexType);
        GR_STATIC_ASSERT(1 == kU16_PathIndexType);
        GR_STATIC_ASSERT(2 == kU32_PathIndexType);
        GR_STATIC_ASSERT(kU32_PathIndexType == kLast_PathIndexType);

        return 1 << type;
    }

    /**
     * Class that generates the paths for a specific range.
     */
    class PathGenerator : public SkRefCnt {
    public:
        virtual int getNumPaths() = 0;
        virtual void generatePath(int index, SkPath* out) = 0;
#ifdef SK_DEBUG
        virtual bool isEqualTo(const SkDescriptor&) const { return false; }
#endif
        virtual ~PathGenerator() {}
    };

    /**
     * Initialize a lazy-loaded path range. This class will generate an SkPath and call
     * onInitPath() for each path within the range before it is drawn for the first time.
     */
    GrPathRange(GrGpu*, PathGenerator*);

    /**
     * Initialize an eager-loaded path range. The subclass is responsible for ensuring all
     * the paths are initialized up front.
     */
    GrPathRange(GrGpu*, int numPaths);

    int getNumPaths() const { return fNumPaths; }
    const PathGenerator* getPathGenerator() const { return fPathGenerator.get(); }

    void loadPathsIfNeeded(const void* indices, PathIndexType, int count) const;

    template<typename IndexType> void loadPathsIfNeeded(const IndexType* indices, int count) const {
        if (!fPathGenerator) {
            return;
        }

        bool didLoadPaths = false;

        for (int i = 0; i < count; ++i) {
            SkASSERT(indices[i] < static_cast<uint32_t>(fNumPaths));

            const int groupIndex = indices[i] / kPathsPerGroup;
            const int groupByte = groupIndex / 8;
            const uint8_t groupBit = 1 << (groupIndex % 8);

            const bool hasPath = SkToBool(fGeneratedPaths[groupByte] & groupBit);
            if (!hasPath) {
                // We track which paths are loaded in groups of kPathsPerGroup. To
                // mark a path as loaded we need to load the entire group.
                const int groupFirstPath = groupIndex * kPathsPerGroup;
                const int groupLastPath = SkTMin(groupFirstPath + kPathsPerGroup, fNumPaths) - 1;

                SkPath path;
                for (int pathIdx = groupFirstPath; pathIdx <= groupLastPath; ++pathIdx) {
                    fPathGenerator->generatePath(pathIdx, &path);
                    this->onInitPath(pathIdx, path);
                }

                fGeneratedPaths[groupByte] |= groupBit;
                didLoadPaths = true;
            }
        }

        if (didLoadPaths) {
            this->didChangeGpuMemorySize();
        }
    }

#ifdef SK_DEBUG
    void assertPathsLoaded(const void* indices, PathIndexType, int count) const;

    template<typename IndexType> void assertPathsLoaded(const IndexType* indices, int count) const {
        if (!fPathGenerator) {
            return;
        }

        for (int i = 0; i < count; ++i) {
            SkASSERT(indices[i] < static_cast<uint32_t>(fNumPaths));

            const int groupIndex = indices[i] / kPathsPerGroup;
            const int groupByte = groupIndex / 8;
            const uint8_t groupBit = 1 << (groupIndex % 8);

            SkASSERT(fGeneratedPaths[groupByte] & groupBit);
        }
    }

    virtual bool isEqualTo(const SkDescriptor& desc) const {
        return nullptr != fPathGenerator.get() && fPathGenerator->isEqualTo(desc);
    }
#endif
protected:
    // Initialize a path in the range before drawing. This is only called when
    // fPathGenerator is non-null. The child class need not call didChangeGpuMemorySize(),
    // GrPathRange will take care of that after the call is complete.
    virtual void onInitPath(int index, const SkPath&) const = 0;

private:
    enum {
        kPathsPerGroup = 16 // Paths get tracked in groups of 16 for lazy loading.
    };

    mutable SkAutoTUnref<PathGenerator> fPathGenerator;
    mutable SkTArray<uint8_t, true /*MEM_COPY*/> fGeneratedPaths;
    const int fNumPaths;

    typedef GrGpuResource INHERITED;
};

#endif