aboutsummaryrefslogtreecommitdiffhomepage
path: root/include/gpu/GrProgramElement.h
blob: 8305c5027b0f17a3decb86049169b896583ba8fe (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
/*
 * 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 GrProgramElement_DEFINED
#define GrProgramElement_DEFINED

#include "SkRefCnt.h"
#include "SkTArray.h"

class GrGpuResourceRef;

/**
 * Base class for GrProcessor. This exists to manage transitioning a GrProcessor from being owned by
 * a client to being scheduled for execution. While a GrProcessor is ref'ed by drawing code its
 * GrGpu resources must also be ref'ed to prevent incorrectly recycling them through the cache.
 * However, once the GrProcessor is baked into a GrPipeline and the drawing code has stopped ref'ing
 * it, it's internal resources can be recycled in some cases.
 *
 * We track this using two types of refs on GrProgramElement. A regular ref is owned by any client
 * that may continue to issue draws that use the GrProgramElement. The GrPipeline owns "pending
 * executions" instead of refs. A pending execution is cleared by ~GrPipeline().
 *
 * While a GrProgramElement is ref'ed any resources it owns are also ref'ed. However, once it gets
 * into the state where it has pending executions AND no refs then it converts its ownership of
 * its GrGpuResources from refs to pending IOs. The pending IOs allow the cache to track when it is
 * safe to recycle a resource even though we still have buffered GrBatches that read or write to the
 * the resource.
 *
 * To make this work all GrGpuResource objects owned by a GrProgramElement or derived classes
 * (either directly or indirectly) must be wrapped in a GrGpuResourceRef and registered with the
 * GrProgramElement using addGpuResource(). This allows the regular refs to be converted to pending
 * IO events when the program element is scheduled for deferred execution.
 *
 * Moreover, a GrProgramElement that in turn owns other GrProgramElements must convert its ownership
 * of its children to pending executions when its ref count reaches zero so that the GrGpuResources
 * owned by the children GrProgramElements are correctly converted from ownership by ref to
 * ownership by pending IO. Any GrProgramElement hierarchy is managed by subclasses which must
 * implement notifyRefCntIsZero() in order to convert refs of children to pending executions.
 */
class GrProgramElement : public SkNoncopyable {
public:
    virtual ~GrProgramElement() {
        // fRefCnt can be one when an effect is created statically using GR_CREATE_STATIC_EFFECT
        SkASSERT((0 == fRefCnt || 1 == fRefCnt) && 0 == fPendingExecutions);
        // Set to invalid values.
        SkDEBUGCODE(fRefCnt = fPendingExecutions = -10;)
    }

    void ref() const {
        this->validate();
        // Once the ref cnt reaches zero it should never be ref'ed again.
        SkASSERT(fRefCnt > 0);
        ++fRefCnt;
        this->validate();
    }

    void unref() const {
        this->validate();
        --fRefCnt;
        if (0 == fRefCnt) {
            this->notifyRefCntIsZero();
            if (0 == fPendingExecutions) {
                delete this;
                return;
            } else {
                this->removeRefs();
            }
        }
        this->validate();
    }

    /**
     * Gets an id that is unique for this GrProgramElement object. This will never return 0.
     */
    uint32_t getUniqueID() const { return fUniqueID; }

    void validate() const {
#ifdef SK_DEBUG
        SkASSERT(fRefCnt >= 0);
        SkASSERT(fPendingExecutions >= 0);
        SkASSERT(fRefCnt + fPendingExecutions > 0);
#endif
    }

protected:
    GrProgramElement() : fRefCnt(1), fPendingExecutions(0), fUniqueID(CreateUniqueID()) {}

    /** Subclasses registers their resources using this function. It is assumed the GrProgramResouce
        is and will remain owned by the subclass and this function will retain a raw ptr. Once a
        GrGpuResourceRef is registered its setResource must not be called.
     */
    void addGpuResource(const GrGpuResourceRef* res) {
        fGpuResources.push_back(res);
    }

    void addPendingExecution() const {
        this->validate();
        SkASSERT(fRefCnt > 0);
        if (0 == fPendingExecutions) {
            this->addPendingIOs();
        }
        ++fPendingExecutions;
        this->validate();
    }

    void completedExecution() const {
        this->validate();
        --fPendingExecutions;
        if (0 == fPendingExecutions) {
            if (0 == fRefCnt) {
                delete this;
                return;
            } else {
                this->pendingIOComplete();
            }
        }
        this->validate();
    }

private:
    /** This will be called when the ref cnt is zero. The object may or may not have pending
        executions. */
    virtual void notifyRefCntIsZero() const = 0;

    static uint32_t CreateUniqueID();

    void removeRefs() const;
    void addPendingIOs() const;
    void pendingIOComplete() const;

    mutable int32_t fRefCnt;
    // Count of deferred executions not yet issued to the 3D API.
    mutable int32_t fPendingExecutions;
    uint32_t        fUniqueID;

    SkSTArray<4, const GrGpuResourceRef*, true> fGpuResources;

    // Only this class can access addPendingExecution() and completedExecution().
    template <typename T> friend class GrPendingProgramElement;

    typedef SkNoncopyable INHERITED;
};

#endif