summaryrefslogtreecommitdiff
path: root/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
blob: 7534548d4ab77bf6a37907d1a74dbff7fa2f21c6 (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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <compositionengine/Output.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/LayerState.h>

#include <chrono>
#include <numeric>
#include <vector>

namespace android {

namespace renderengine {
class RenderEngine;
} // namespace renderengine

namespace compositionengine::impl::planner {
using namespace std::chrono_literals;

class LayerState;
class Predictor;

class Flattener {
public:
    struct CachedSetRenderSchedulingTunables {
        // This default assumes that rendering a cached set takes about 3ms. That time is then cut
        // in half - the next frame using the cached set would have the same workload, meaning that
        // composition cost is the same. This is best illustrated with the following example:
        //
        // Suppose we're at a 120hz cadence so SurfaceFlinger is budgeted 8.3ms per-frame. If
        // renderCachedSets costs 3ms, then two consecutive frames have timings:
        //
        // First frame: Start at 0ms, end at 6.8ms.
        // renderCachedSets: Start at 6.8ms, end at 9.8ms.
        // Second frame: Start at 9.8ms, end at 16.6ms.
        //
        // Now the second frame won't render a cached set afterwards, but the first frame didn't
        // really steal time from the second frame.
        static const constexpr std::chrono::nanoseconds kDefaultCachedSetRenderDuration = 1500us;

        static const constexpr size_t kDefaultMaxDeferRenderAttempts = 240;

        // Duration allocated for rendering a cached set. If we don't have enough time for rendering
        // a cached set, then rendering is deferred to another frame.
        const std::chrono::nanoseconds cachedSetRenderDuration;
        // Maximum of times that we defer rendering a cached set. If we defer rendering a cached set
        // too many times, then render it anyways so that future frames would benefit from the
        // flattened cached set.
        const size_t maxDeferRenderAttempts;
    };
    Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false,
              std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables =
                      std::nullopt);

    void setDisplaySize(ui::Size size) {
        mDisplaySize = size;
        mTexturePool.setDisplaySize(size);
    }

    NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
                                std::chrono::steady_clock::time_point now);

    // Renders the newest cached sets with the supplied output composition state
    void renderCachedSets(const OutputCompositionState& outputState,
                          std::optional<std::chrono::steady_clock::time_point> renderDeadline);

    void dump(std::string& result) const;
    void dumpLayers(std::string& result) const;

    const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; }

private:
    size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;

    void resetActivities(NonBufferHash, std::chrono::steady_clock::time_point now);

    NonBufferHash computeLayersHash() const;

    bool mergeWithCachedSets(const std::vector<const LayerState*>& layers,
                             std::chrono::steady_clock::time_point now);

    // A Run is a sequence of CachedSets, which is a candidate for flattening into a single
    // CachedSet. Because it is wasteful to flatten 1 CachedSet, a Run must contain more than 1
    // CachedSet
    class Run {
    public:
        // A builder for a Run, to aid in construction
        class Builder {
        private:
            std::vector<CachedSet>::const_iterator mStart;
            std::vector<size_t> mLengths;
            const CachedSet* mHolePunchCandidate = nullptr;
            const CachedSet* mBlurringLayer = nullptr;

        public:
            // Initializes a Builder a CachedSet to start from.
            // This start iterator must be an iterator for mLayers
            void init(const std::vector<CachedSet>::const_iterator& start) {
                mStart = start;
                mLengths.push_back(start->getLayerCount());
            }

            // Appends a new CachedSet to the end of the run
            // The provided length must be the size of the next sequential CachedSet in layers
            void append(size_t length) { mLengths.push_back(length); }

            // Sets the hole punch candidate for the Run.
            void setHolePunchCandidate(const CachedSet* holePunchCandidate) {
                mHolePunchCandidate = holePunchCandidate;
            }

            void setBlurringLayer(const CachedSet* blurringLayer) {
                mBlurringLayer = blurringLayer;
            }

            // Builds a Run instance, if a valid Run may be built.
            std::optional<Run> validateAndBuild() {
                if (mLengths.size() <= 1) {
                    return std::nullopt;
                }

                return Run(mStart,
                           std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
                                       [](size_t left, size_t right) { return left + right; }),
                           mHolePunchCandidate, mBlurringLayer);
            }

            void reset() { *this = {}; }
        };

        // Gets the starting CachedSet of this run.
        // This is an iterator into mLayers
        const std::vector<CachedSet>::const_iterator& getStart() const { return mStart; }
        // Gets the total number of layers encompassing this Run.
        size_t getLayerLength() const { return mLength; }
        // Gets the hole punch candidate for this Run.
        const CachedSet* getHolePunchCandidate() const { return mHolePunchCandidate; }
        const CachedSet* getBlurringLayer() const { return mBlurringLayer; }

    private:
        Run(std::vector<CachedSet>::const_iterator start, size_t length,
            const CachedSet* holePunchCandidate, const CachedSet* blurringLayer)
              : mStart(start),
                mLength(length),
                mHolePunchCandidate(holePunchCandidate),
                mBlurringLayer(blurringLayer) {}
        const std::vector<CachedSet>::const_iterator mStart;
        const size_t mLength;
        const CachedSet* const mHolePunchCandidate;
        const CachedSet* const mBlurringLayer;

        friend class Builder;
    };

    std::vector<Run> findCandidateRuns(std::chrono::steady_clock::time_point now) const;

    std::optional<Run> findBestRun(std::vector<Run>& runs) const;

    void buildCachedSets(std::chrono::steady_clock::time_point now);

    renderengine::RenderEngine& mRenderEngine;
    const bool mEnableHolePunch;
    const std::optional<CachedSetRenderSchedulingTunables> mCachedSetRenderSchedulingTunables;

    TexturePool mTexturePool;

protected:
    // mNewCachedSet must be destroyed before mTexturePool is.
    std::optional<CachedSet> mNewCachedSet;

private:
    ui::Size mDisplaySize;

    NonBufferHash mCurrentGeometry;
    std::chrono::steady_clock::time_point mLastGeometryUpdate;

    std::vector<CachedSet> mLayers;

    // Statistics
    size_t mUnflattenedDisplayCost = 0;
    size_t mFlattenedDisplayCost = 0;
    std::unordered_map<size_t, size_t> mInitialLayerCounts;
    std::unordered_map<size_t, size_t> mFinalLayerCounts;
    size_t mCachedSetCreationCount = 0;
    size_t mCachedSetCreationCost = 0;
    std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
    std::chrono::nanoseconds mActiveLayerTimeout = kActiveLayerTimeout;

    static constexpr auto kActiveLayerTimeout = std::chrono::nanoseconds(150ms);
};

} // namespace compositionengine::impl::planner
} // namespace android