1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h"
9cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h"
10cb93a386Sopenharmony_ci#include "src/gpu/GrMemoryPool.h"
11cb93a386Sopenharmony_ci#include "src/gpu/GrOpFlushState.h"
12cb93a386Sopenharmony_ci#include "src/gpu/GrProxyProvider.h"
13cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h"
14cb93a386Sopenharmony_ci#include "src/gpu/ops/GrOp.h"
15cb93a386Sopenharmony_ci#include "src/gpu/ops/OpsTask.h"
16cb93a386Sopenharmony_ci#include "tests/Test.h"
17cb93a386Sopenharmony_ci#include <iterator>
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ci// We create Ops that write a value into a range of a buffer. We create ranges from
20cb93a386Sopenharmony_ci// kNumOpPositions starting positions x kRanges canonical ranges. We repeat each range kNumRepeats
21cb93a386Sopenharmony_ci// times (with a different value written by each of the repeats).
22cb93a386Sopenharmony_cinamespace {
23cb93a386Sopenharmony_cistruct Range {
24cb93a386Sopenharmony_ci    unsigned fOffset;
25cb93a386Sopenharmony_ci    unsigned fLength;
26cb93a386Sopenharmony_ci};
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_cistatic constexpr int kNumOpPositions = 4;
29cb93a386Sopenharmony_cistatic constexpr Range kRanges[] = {{0, 4,}, {1, 2}};
30cb93a386Sopenharmony_cistatic constexpr int kNumRanges = (int)SK_ARRAY_COUNT(kRanges);
31cb93a386Sopenharmony_cistatic constexpr int kNumRepeats = 2;
32cb93a386Sopenharmony_cistatic constexpr int kNumOps = kNumRepeats * kNumOpPositions * kNumRanges;
33cb93a386Sopenharmony_ci
34cb93a386Sopenharmony_cistatic constexpr uint64_t fact(int n) {
35cb93a386Sopenharmony_ci    assert(n > 0);
36cb93a386Sopenharmony_ci    return n > 1 ? n * fact(n - 1) : 1;
37cb93a386Sopenharmony_ci}
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_ci// How wide should our result buffer be to hold values written by the ranges of the ops.
40cb93a386Sopenharmony_cistatic constexpr unsigned result_width() {
41cb93a386Sopenharmony_ci    unsigned maxLength = 0;
42cb93a386Sopenharmony_ci    for (size_t i = 0; i < kNumRanges; ++i) {
43cb93a386Sopenharmony_ci        maxLength = maxLength > kRanges[i].fLength ? maxLength : kRanges[i].fLength;
44cb93a386Sopenharmony_ci    }
45cb93a386Sopenharmony_ci    return kNumOpPositions + maxLength - 1;
46cb93a386Sopenharmony_ci}
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci// Number of possible allowable binary chainings among the kNumOps ops.
49cb93a386Sopenharmony_cistatic constexpr int kNumCombinableValues = fact(kNumOps) / fact(kNumOps - 2);
50cb93a386Sopenharmony_ciusing Combinable = std::array<GrOp::CombineResult, kNumCombinableValues>;
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci/**
53cb93a386Sopenharmony_ci * The index in Combinable for the result for combining op 'b' into op 'a', i.e. the result of
54cb93a386Sopenharmony_ci * op[a]->combineIfPossible(op[b]).
55cb93a386Sopenharmony_ci */
56cb93a386Sopenharmony_ciint64_t combinable_index(int a, int b) {
57cb93a386Sopenharmony_ci    SkASSERT(b != a);
58cb93a386Sopenharmony_ci    // Each index gets kNumOps - 1 contiguous bools
59cb93a386Sopenharmony_ci    int64_t aOffset = a * (kNumOps - 1);
60cb93a386Sopenharmony_ci    // Within a's range we have one value each other op, but not one for a itself.
61cb93a386Sopenharmony_ci    int64_t bIdxInA = b < a ? b : b - 1;
62cb93a386Sopenharmony_ci    return aOffset + bIdxInA;
63cb93a386Sopenharmony_ci}
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci/**
66cb93a386Sopenharmony_ci * Creates a legal set of combinability results for the ops. The likelihood that any two ops
67cb93a386Sopenharmony_ci * in a group can merge is randomly chosen.
68cb93a386Sopenharmony_ci */
69cb93a386Sopenharmony_cistatic void init_combinable(int numGroups, Combinable* combinable, SkRandom* random) {
70cb93a386Sopenharmony_ci    SkScalar mergeProbability = random->nextUScalar1();
71cb93a386Sopenharmony_ci    std::fill_n(combinable->begin(), kNumCombinableValues, GrOp::CombineResult::kCannotCombine);
72cb93a386Sopenharmony_ci    SkTDArray<int> groups[kNumOps];
73cb93a386Sopenharmony_ci    for (int i = 0; i < kNumOps; ++i) {
74cb93a386Sopenharmony_ci        auto& group = groups[random->nextULessThan(numGroups)];
75cb93a386Sopenharmony_ci        for (int g = 0; g < group.count(); ++g) {
76cb93a386Sopenharmony_ci            int j = group[g];
77cb93a386Sopenharmony_ci            if (random->nextUScalar1() < mergeProbability) {
78cb93a386Sopenharmony_ci                (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMerged;
79cb93a386Sopenharmony_ci            } else {
80cb93a386Sopenharmony_ci                (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMayChain;
81cb93a386Sopenharmony_ci            }
82cb93a386Sopenharmony_ci            if (random->nextUScalar1() < mergeProbability) {
83cb93a386Sopenharmony_ci                (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMerged;
84cb93a386Sopenharmony_ci            } else {
85cb93a386Sopenharmony_ci                (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMayChain;
86cb93a386Sopenharmony_ci            }
87cb93a386Sopenharmony_ci        }
88cb93a386Sopenharmony_ci        group.push_back(i);
89cb93a386Sopenharmony_ci    }
90cb93a386Sopenharmony_ci}
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci/**
93cb93a386Sopenharmony_ci * A simple test op. It has an integer position, p. When it executes it writes p into an array
94cb93a386Sopenharmony_ci * of ints at index p and p+1. It takes a bitfield that indicates allowed pair-wise chainings.
95cb93a386Sopenharmony_ci */
96cb93a386Sopenharmony_ciclass TestOp : public GrOp {
97cb93a386Sopenharmony_cipublic:
98cb93a386Sopenharmony_ci    DEFINE_OP_CLASS_ID
99cb93a386Sopenharmony_ci
100cb93a386Sopenharmony_ci    static GrOp::Owner Make(GrRecordingContext* context, int value, const Range& range,
101cb93a386Sopenharmony_ci                            int result[], const Combinable* combinable) {
102cb93a386Sopenharmony_ci        return GrOp::Make<TestOp>(context, value, range, result, combinable);
103cb93a386Sopenharmony_ci    }
104cb93a386Sopenharmony_ci
105cb93a386Sopenharmony_ci    const char* name() const override { return "TestOp"; }
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci    void writeResult(int result[]) const {
108cb93a386Sopenharmony_ci        for (const auto& op : ChainRange<TestOp>(this)) {
109cb93a386Sopenharmony_ci            for (const auto& vr : op.fValueRanges) {
110cb93a386Sopenharmony_ci                for (unsigned i = 0; i < vr.fRange.fLength; ++i) {
111cb93a386Sopenharmony_ci                    result[vr.fRange.fOffset + i] = vr.fValue;
112cb93a386Sopenharmony_ci                }
113cb93a386Sopenharmony_ci            }
114cb93a386Sopenharmony_ci        }
115cb93a386Sopenharmony_ci    }
116cb93a386Sopenharmony_ci
117cb93a386Sopenharmony_ciprivate:
118cb93a386Sopenharmony_ci    friend class ::GrOp;  // for ctor
119cb93a386Sopenharmony_ci
120cb93a386Sopenharmony_ci    TestOp(int value, const Range& range, int result[], const Combinable* combinable)
121cb93a386Sopenharmony_ci            : INHERITED(ClassID()), fResult(result), fCombinable(combinable) {
122cb93a386Sopenharmony_ci        fValueRanges.push_back({value, range});
123cb93a386Sopenharmony_ci        this->setBounds(SkRect::MakeXYWH(range.fOffset, 0, range.fOffset + range.fLength, 1),
124cb93a386Sopenharmony_ci                        HasAABloat::kNo, IsHairline::kNo);
125cb93a386Sopenharmony_ci    }
126cb93a386Sopenharmony_ci
127cb93a386Sopenharmony_ci    void onPrePrepare(GrRecordingContext*,
128cb93a386Sopenharmony_ci                      const GrSurfaceProxyView& writeView,
129cb93a386Sopenharmony_ci                      GrAppliedClip*,
130cb93a386Sopenharmony_ci                      const GrDstProxyView&,
131cb93a386Sopenharmony_ci                      GrXferBarrierFlags renderPassXferBarriers,
132cb93a386Sopenharmony_ci                      GrLoadOp colorLoadOp) override {}
133cb93a386Sopenharmony_ci
134cb93a386Sopenharmony_ci    void onPrepare(GrOpFlushState*) override {}
135cb93a386Sopenharmony_ci
136cb93a386Sopenharmony_ci    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override {
137cb93a386Sopenharmony_ci        for (auto& op : ChainRange<TestOp>(this)) {
138cb93a386Sopenharmony_ci            op.writeResult(fResult);
139cb93a386Sopenharmony_ci        }
140cb93a386Sopenharmony_ci    }
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ci    CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc* arenas, const GrCaps&) override {
143cb93a386Sopenharmony_ci        // This op doesn't use the arenas, but make sure the OpsTask is sending it
144cb93a386Sopenharmony_ci        SkASSERT(arenas);
145cb93a386Sopenharmony_ci        (void) arenas;
146cb93a386Sopenharmony_ci        auto that = t->cast<TestOp>();
147cb93a386Sopenharmony_ci        int v0 = fValueRanges[0].fValue;
148cb93a386Sopenharmony_ci        int v1 = that->fValueRanges[0].fValue;
149cb93a386Sopenharmony_ci        auto result = (*fCombinable)[combinable_index(v0, v1)];
150cb93a386Sopenharmony_ci        if (result == GrOp::CombineResult::kMerged) {
151cb93a386Sopenharmony_ci            std::move(that->fValueRanges.begin(), that->fValueRanges.end(),
152cb93a386Sopenharmony_ci                      std::back_inserter(fValueRanges));
153cb93a386Sopenharmony_ci        }
154cb93a386Sopenharmony_ci        return result;
155cb93a386Sopenharmony_ci    }
156cb93a386Sopenharmony_ci
157cb93a386Sopenharmony_ci    struct ValueRange {
158cb93a386Sopenharmony_ci        int fValue;
159cb93a386Sopenharmony_ci        Range fRange;
160cb93a386Sopenharmony_ci    };
161cb93a386Sopenharmony_ci    std::vector<ValueRange> fValueRanges;
162cb93a386Sopenharmony_ci    int* fResult;
163cb93a386Sopenharmony_ci    const Combinable* fCombinable;
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_ci    using INHERITED = GrOp;
166cb93a386Sopenharmony_ci};
167cb93a386Sopenharmony_ci}  // namespace
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci/**
170cb93a386Sopenharmony_ci * Tests adding kNumOps to an op list with all possible allowed chaining configurations. Tests
171cb93a386Sopenharmony_ci * adding the ops in all possible orders and verifies that the chained executions don't violate
172cb93a386Sopenharmony_ci * painter's order.
173cb93a386Sopenharmony_ci */
174cb93a386Sopenharmony_ciDEF_GPUTEST(OpChainTest, reporter, /*ctxInfo*/) {
175cb93a386Sopenharmony_ci    sk_sp<GrDirectContext> dContext = GrDirectContext::MakeMock(nullptr);
176cb93a386Sopenharmony_ci    SkASSERT(dContext);
177cb93a386Sopenharmony_ci    const GrCaps* caps = dContext->priv().caps();
178cb93a386Sopenharmony_ci    static constexpr SkISize kDims = {kNumOps + 1, 1};
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_ci    const GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
181cb93a386Sopenharmony_ci                                                                 GrRenderable::kYes);
182cb93a386Sopenharmony_ci
183cb93a386Sopenharmony_ci    static const GrSurfaceOrigin kOrigin = kTopLeft_GrSurfaceOrigin;
184cb93a386Sopenharmony_ci    auto proxy = dContext->priv().proxyProvider()->createProxy(
185cb93a386Sopenharmony_ci            format, kDims, GrRenderable::kYes, 1, GrMipmapped::kNo, SkBackingFit::kExact,
186cb93a386Sopenharmony_ci            SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone);
187cb93a386Sopenharmony_ci    SkASSERT(proxy);
188cb93a386Sopenharmony_ci    proxy->instantiate(dContext->priv().resourceProvider());
189cb93a386Sopenharmony_ci
190cb93a386Sopenharmony_ci    GrSwizzle writeSwizzle = caps->getWriteSwizzle(format, GrColorType::kRGBA_8888);
191cb93a386Sopenharmony_ci
192cb93a386Sopenharmony_ci    int result[result_width()];
193cb93a386Sopenharmony_ci    int validResult[result_width()];
194cb93a386Sopenharmony_ci
195cb93a386Sopenharmony_ci    int permutation[kNumOps];
196cb93a386Sopenharmony_ci    for (int i = 0; i < kNumOps; ++i) {
197cb93a386Sopenharmony_ci        permutation[i] = i;
198cb93a386Sopenharmony_ci    }
199cb93a386Sopenharmony_ci    // Op order permutations.
200cb93a386Sopenharmony_ci    static constexpr int kNumPermutations = 100;
201cb93a386Sopenharmony_ci    // For a given number of chainability groups, this is the number of random combinability reuslts
202cb93a386Sopenharmony_ci    // we will test.
203cb93a386Sopenharmony_ci    static constexpr int kNumCombinabilitiesPerGrouping = 20;
204cb93a386Sopenharmony_ci    SkRandom random;
205cb93a386Sopenharmony_ci    bool repeat = false;
206cb93a386Sopenharmony_ci    Combinable combinable;
207cb93a386Sopenharmony_ci    GrDrawingManager* drawingMgr = dContext->priv().drawingManager();
208cb93a386Sopenharmony_ci    sk_sp<GrArenas> arenas = sk_make_sp<GrArenas>();
209cb93a386Sopenharmony_ci    for (int p = 0; p < kNumPermutations; ++p) {
210cb93a386Sopenharmony_ci        for (int i = 0; i < kNumOps - 2 && !repeat; ++i) {
211cb93a386Sopenharmony_ci            // The current implementation of nextULessThan() is biased. :(
212cb93a386Sopenharmony_ci            unsigned j = i + random.nextULessThan(kNumOps - i);
213cb93a386Sopenharmony_ci            std::swap(permutation[i], permutation[j]);
214cb93a386Sopenharmony_ci        }
215cb93a386Sopenharmony_ci        // g is the number of chainable groups that we partition the ops into.
216cb93a386Sopenharmony_ci        for (int g = 1; g < kNumOps; ++g) {
217cb93a386Sopenharmony_ci            for (int c = 0; c < kNumCombinabilitiesPerGrouping; ++c) {
218cb93a386Sopenharmony_ci                init_combinable(g, &combinable, &random);
219cb93a386Sopenharmony_ci                GrTokenTracker tracker;
220cb93a386Sopenharmony_ci                GrOpFlushState flushState(dContext->priv().getGpu(),
221cb93a386Sopenharmony_ci                                          dContext->priv().resourceProvider(),
222cb93a386Sopenharmony_ci                                          &tracker);
223cb93a386Sopenharmony_ci                skgpu::v1::OpsTask opsTask(drawingMgr,
224cb93a386Sopenharmony_ci                                           GrSurfaceProxyView(proxy, kOrigin, writeSwizzle),
225cb93a386Sopenharmony_ci                                           dContext->priv().auditTrail(),
226cb93a386Sopenharmony_ci                                           arenas);
227cb93a386Sopenharmony_ci                // This assumes the particular values of kRanges.
228cb93a386Sopenharmony_ci                std::fill_n(result, result_width(), -1);
229cb93a386Sopenharmony_ci                std::fill_n(validResult, result_width(), -1);
230cb93a386Sopenharmony_ci                for (int i = 0; i < kNumOps; ++i) {
231cb93a386Sopenharmony_ci                    int value = permutation[i];
232cb93a386Sopenharmony_ci                    // factor out the repeats and then use the canonical starting position and range
233cb93a386Sopenharmony_ci                    // to determine an actual range.
234cb93a386Sopenharmony_ci                    int j = value % (kNumRanges * kNumOpPositions);
235cb93a386Sopenharmony_ci                    int pos = j % kNumOpPositions;
236cb93a386Sopenharmony_ci                    Range range = kRanges[j / kNumOpPositions];
237cb93a386Sopenharmony_ci                    range.fOffset += pos;
238cb93a386Sopenharmony_ci                    auto op = TestOp::Make(dContext.get(), value, range, result, &combinable);
239cb93a386Sopenharmony_ci                    TestOp* testOp = (TestOp*)op.get();
240cb93a386Sopenharmony_ci                    testOp->writeResult(validResult);
241cb93a386Sopenharmony_ci                    opsTask.addOp(drawingMgr, std::move(op),
242cb93a386Sopenharmony_ci                                  GrTextureResolveManager(dContext->priv().drawingManager()),
243cb93a386Sopenharmony_ci                                  *caps);
244cb93a386Sopenharmony_ci                }
245cb93a386Sopenharmony_ci                opsTask.makeClosed(dContext.get());
246cb93a386Sopenharmony_ci                opsTask.prepare(&flushState);
247cb93a386Sopenharmony_ci                opsTask.execute(&flushState);
248cb93a386Sopenharmony_ci                opsTask.endFlush(drawingMgr);
249cb93a386Sopenharmony_ci                opsTask.disown(drawingMgr);
250cb93a386Sopenharmony_ci#if 0  // Useful to repeat a random configuration that fails the test while debugger attached.
251cb93a386Sopenharmony_ci                if (!std::equal(result, result + result_width(), validResult)) {
252cb93a386Sopenharmony_ci                    repeat = true;
253cb93a386Sopenharmony_ci                }
254cb93a386Sopenharmony_ci#endif
255cb93a386Sopenharmony_ci                (void)repeat;
256cb93a386Sopenharmony_ci                REPORTER_ASSERT(reporter, std::equal(result, result + result_width(), validResult));
257cb93a386Sopenharmony_ci            }
258cb93a386Sopenharmony_ci        }
259cb93a386Sopenharmony_ci    }
260cb93a386Sopenharmony_ci}
261