blob: 6aecafc1e77f94f410247d0c47ba61d6166f9862 [file] [log] [blame]
Brian Salomon764e5462018-08-21 12:07:00 -04001/*
2 * Copyright 2018 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
Robert Phillips0c5bb2f2020-07-17 15:40:13 -04008#include "include/gpu/GrDirectContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "src/gpu/GrContextPriv.h"
10#include "src/gpu/GrMemoryPool.h"
11#include "src/gpu/GrOpFlushState.h"
Greg Danielf41b2bd2019-08-22 16:19:24 -040012#include "src/gpu/GrOpsTask.h"
Robert Phillipse19babf2020-04-06 13:57:30 -040013#include "src/gpu/GrProxyProvider.h"
Robert Phillips0c5bb2f2020-07-17 15:40:13 -040014#include "src/gpu/GrRecordingContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/gpu/ops/GrOp.h"
16#include "tests/Test.h"
Brian Salomon764e5462018-08-21 12:07:00 -040017
Brian Salomona7682c82018-10-24 10:04:37 -040018// We create Ops that write a value into a range of a buffer. We create ranges from
19// kNumOpPositions starting positions x kRanges canonical ranges. We repeat each range kNumRepeats
20// times (with a different value written by each of the repeats).
21namespace {
22struct Range {
23 unsigned fOffset;
24 unsigned fLength;
25};
Brian Salomon764e5462018-08-21 12:07:00 -040026
Brian Salomona7682c82018-10-24 10:04:37 -040027static constexpr int kNumOpPositions = 4;
28static constexpr Range kRanges[] = {{0, 4,}, {1, 2}};
29static constexpr int kNumRanges = (int)SK_ARRAY_COUNT(kRanges);
30static constexpr int kNumRepeats = 2;
31static constexpr int kNumOps = kNumRepeats * kNumOpPositions * kNumRanges;
32
33static constexpr uint64_t fact(int n) {
Brian Salomon764e5462018-08-21 12:07:00 -040034 assert(n > 0);
35 return n > 1 ? n * fact(n - 1) : 1;
36}
37
Brian Salomona7682c82018-10-24 10:04:37 -040038// How wide should our result buffer be to hold values written by the ranges of the ops.
39static constexpr unsigned result_width() {
40 unsigned maxLength = 0;
41 for (size_t i = 0; i < kNumRanges; ++i) {
42 maxLength = maxLength > kRanges[i].fLength ? maxLength : kRanges[i].fLength;
43 }
44 return kNumOpPositions + maxLength - 1;
Brian Salomon764e5462018-08-21 12:07:00 -040045}
46
Brian Salomona7682c82018-10-24 10:04:37 -040047// Number of possible allowable binary chainings among the kNumOps ops.
48static constexpr int kNumCombinableValues = fact(kNumOps) / fact(kNumOps - 2);
49using Combinable = std::array<GrOp::CombineResult, kNumCombinableValues>;
50
Brian Salomon588cec72018-11-14 13:56:37 -050051/**
52 * The index in Combinable for the result for combining op 'b' into op 'a', i.e. the result of
53 * op[a]->combineIfPossible(op[b]).
54 */
55int64_t combinable_index(int a, int b) {
Brian Salomona7682c82018-10-24 10:04:37 -040056 SkASSERT(b != a);
57 // Each index gets kNumOps - 1 contiguous bools
Brian Salomon588cec72018-11-14 13:56:37 -050058 int64_t aOffset = a * (kNumOps - 1);
Brian Salomona7682c82018-10-24 10:04:37 -040059 // Within a's range we have one value each other op, but not one for a itself.
60 int64_t bIdxInA = b < a ? b : b - 1;
Brian Salomon588cec72018-11-14 13:56:37 -050061 return aOffset + bIdxInA;
62}
63
64/**
65 * Creates a legal set of combinability results for the ops. The likelihood that any two ops
66 * in a group can merge is randomly chosen.
67 */
68static void init_combinable(int numGroups, Combinable* combinable, SkRandom* random) {
69 SkScalar mergeProbability = random->nextUScalar1();
70 std::fill_n(combinable->begin(), kNumCombinableValues, GrOp::CombineResult::kCannotCombine);
71 SkTDArray<int> groups[kNumOps];
72 for (int i = 0; i < kNumOps; ++i) {
73 auto& group = groups[random->nextULessThan(numGroups)];
74 for (int g = 0; g < group.count(); ++g) {
75 int j = group[g];
76 if (random->nextUScalar1() < mergeProbability) {
77 (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMerged;
78 } else {
79 (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMayChain;
80 }
81 if (random->nextUScalar1() < mergeProbability) {
82 (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMerged;
83 } else {
84 (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMayChain;
85 }
86 }
87 group.push_back(i);
88 }
Brian Salomona7682c82018-10-24 10:04:37 -040089}
90
Brian Salomon764e5462018-08-21 12:07:00 -040091/**
92 * A simple test op. It has an integer position, p. When it executes it writes p into an array
93 * of ints at index p and p+1. It takes a bitfield that indicates allowed pair-wise chainings.
94 */
95class TestOp : public GrOp {
96public:
97 DEFINE_OP_CLASS_ID
98
Robert Phillips0c5bb2f2020-07-17 15:40:13 -040099 static std::unique_ptr<TestOp> Make(GrRecordingContext* context, int value, const Range& range,
Brian Salomona7682c82018-10-24 10:04:37 -0400100 int result[], const Combinable* combinable) {
Robert Phillips9da87e02019-02-04 13:26:26 -0500101 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Brian Salomona7682c82018-10-24 10:04:37 -0400102 return pool->allocate<TestOp>(value, range, result, combinable);
Brian Salomon764e5462018-08-21 12:07:00 -0400103 }
104
105 const char* name() const override { return "TestOp"; }
106
Brian Salomona7682c82018-10-24 10:04:37 -0400107 void writeResult(int result[]) const {
108 for (const auto& op : ChainRange<TestOp>(this)) {
109 for (const auto& vr : op.fValueRanges) {
110 for (unsigned i = 0; i < vr.fRange.fLength; ++i) {
111 result[vr.fRange.fOffset + i] = vr.fValue;
112 }
113 }
114 }
115 }
Brian Salomon764e5462018-08-21 12:07:00 -0400116
117private:
118 friend class ::GrOpMemoryPool; // for ctor
119
Brian Salomona7682c82018-10-24 10:04:37 -0400120 TestOp(int value, const Range& range, int result[], const Combinable* combinable)
121 : INHERITED(ClassID()), fResult(result), fCombinable(combinable) {
122 fValueRanges.push_back({value, range});
123 this->setBounds(SkRect::MakeXYWH(range.fOffset, 0, range.fOffset + range.fLength, 1),
Greg Daniel5faf4742019-10-01 15:14:44 -0400124 HasAABloat::kNo, IsHairline::kNo);
Brian Salomon764e5462018-08-21 12:07:00 -0400125 }
126
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400127 void onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400128 const GrSurfaceProxyView* writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400129 GrAppliedClip*,
130 const GrXferProcessor::DstProxyView&) override {}
131
Brian Salomon764e5462018-08-21 12:07:00 -0400132 void onPrepare(GrOpFlushState*) override {}
133
Brian Salomon588cec72018-11-14 13:56:37 -0500134 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override {
Brian Salomon764e5462018-08-21 12:07:00 -0400135 for (auto& op : ChainRange<TestOp>(this)) {
136 op.writeResult(fResult);
137 }
138 }
139
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500140 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas* arenas,
141 const GrCaps&) override {
142 // This op doesn't use the arenas, but make sure the GrOpsTask is sending it
143 SkASSERT(arenas);
144 (void) arenas;
Brian Salomona7682c82018-10-24 10:04:37 -0400145 auto that = t->cast<TestOp>();
Brian Salomon588cec72018-11-14 13:56:37 -0500146 int v0 = fValueRanges[0].fValue;
147 int v1 = that->fValueRanges[0].fValue;
148 auto result = (*fCombinable)[combinable_index(v0, v1)];
Brian Salomona7682c82018-10-24 10:04:37 -0400149 if (result == GrOp::CombineResult::kMerged) {
150 std::move(that->fValueRanges.begin(), that->fValueRanges.end(),
151 std::back_inserter(fValueRanges));
Brian Salomona7682c82018-10-24 10:04:37 -0400152 }
153 return result;
Brian Salomon764e5462018-08-21 12:07:00 -0400154 }
155
Brian Salomona7682c82018-10-24 10:04:37 -0400156 struct ValueRange {
157 int fValue;
158 Range fRange;
159 };
160 std::vector<ValueRange> fValueRanges;
Brian Salomon764e5462018-08-21 12:07:00 -0400161 int* fResult;
Brian Salomona7682c82018-10-24 10:04:37 -0400162 const Combinable* fCombinable;
Brian Salomon764e5462018-08-21 12:07:00 -0400163
164 typedef GrOp INHERITED;
165};
166} // namespace
167
168/**
169 * Tests adding kNumOps to an op list with all possible allowed chaining configurations. Tests
170 * adding the ops in all possible orders and verifies that the chained executions don't violate
171 * painter's order.
172 */
173DEF_GPUTEST(OpChainTest, reporter, /*ctxInfo*/) {
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400174 sk_sp<GrDirectContext> dContext = GrDirectContext::MakeMock(nullptr);
175 SkASSERT(dContext);
176 const GrCaps* caps = dContext->priv().caps();
Brian Salomona56a7462020-02-07 14:17:25 -0500177 static constexpr SkISize kDims = {kNumOps + 1, 1};
Brian Salomon764e5462018-08-21 12:07:00 -0400178
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400179 const GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
180 GrRenderable::kYes);
Greg Daniel4065d452018-11-16 15:43:41 -0500181
Greg Daniel16f5c652019-10-29 11:26:01 -0400182 static const GrSurfaceOrigin kOrigin = kTopLeft_GrSurfaceOrigin;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400183 auto proxy = dContext->priv().proxyProvider()->createProxy(
Brian Salomondf1bd6d2020-03-26 20:37:01 -0400184 format, kDims, GrRenderable::kYes, 1, GrMipMapped::kNo, SkBackingFit::kExact,
Greg Daniel3a365112020-02-14 10:47:18 -0500185 SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone);
Brian Salomon764e5462018-08-21 12:07:00 -0400186 SkASSERT(proxy);
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400187 proxy->instantiate(dContext->priv().resourceProvider());
Greg Daniel16f5c652019-10-29 11:26:01 -0400188
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400189 GrSwizzle writeSwizzle = caps->getWriteSwizzle(format, GrColorType::kRGBA_8888);
Greg Daniel16f5c652019-10-29 11:26:01 -0400190
Brian Salomona7682c82018-10-24 10:04:37 -0400191 int result[result_width()];
192 int validResult[result_width()];
Brian Salomon764e5462018-08-21 12:07:00 -0400193
194 int permutation[kNumOps];
195 for (int i = 0; i < kNumOps; ++i) {
196 permutation[i] = i;
197 }
Brian Salomon588cec72018-11-14 13:56:37 -0500198 // Op order permutations.
Brian Salomona7682c82018-10-24 10:04:37 -0400199 static constexpr int kNumPermutations = 100;
Brian Salomon588cec72018-11-14 13:56:37 -0500200 // For a given number of chainability groups, this is the number of random combinability reuslts
201 // we will test.
202 static constexpr int kNumCombinabilitiesPerGrouping = 20;
Brian Salomona7682c82018-10-24 10:04:37 -0400203 SkRandom random;
204 bool repeat = false;
205 Combinable combinable;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400206 GrDrawingManager* drawingMgr = dContext->priv().drawingManager();
Brian Salomona7682c82018-10-24 10:04:37 -0400207 for (int p = 0; p < kNumPermutations; ++p) {
208 for (int i = 0; i < kNumOps - 2 && !repeat; ++i) {
209 // The current implementation of nextULessThan() is biased. :(
210 unsigned j = i + random.nextULessThan(kNumOps - i);
211 std::swap(permutation[i], permutation[j]);
212 }
Brian Salomon588cec72018-11-14 13:56:37 -0500213 // g is the number of chainable groups that we partition the ops into.
214 for (int g = 1; g < kNumOps; ++g) {
215 for (int c = 0; c < kNumCombinabilitiesPerGrouping; ++c) {
216 init_combinable(g, &combinable, &random);
217 GrTokenTracker tracker;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400218 GrOpFlushState flushState(dContext->priv().getGpu(),
219 dContext->priv().resourceProvider(),
Brian Salomon2c791fc2019-04-02 11:52:03 -0400220 &tracker);
Adlai Hollerd71b7b02020-06-08 15:55:00 -0400221 GrOpsTask opsTask(drawingMgr,
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400222 dContext->priv().arenas(),
Brian Salomon8afde5f2020-04-01 16:22:00 -0400223 GrSurfaceProxyView(proxy, kOrigin, writeSwizzle),
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400224 dContext->priv().auditTrail());
Brian Salomon588cec72018-11-14 13:56:37 -0500225 // This assumes the particular values of kRanges.
226 std::fill_n(result, result_width(), -1);
227 std::fill_n(validResult, result_width(), -1);
228 for (int i = 0; i < kNumOps; ++i) {
229 int value = permutation[i];
230 // factor out the repeats and then use the canonical starting position and range
231 // to determine an actual range.
232 int j = value % (kNumRanges * kNumOpPositions);
233 int pos = j % kNumOpPositions;
234 Range range = kRanges[j / kNumOpPositions];
235 range.fOffset += pos;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400236 auto op = TestOp::Make(dContext.get(), value, range, result, &combinable);
Brian Salomon588cec72018-11-14 13:56:37 -0500237 op->writeResult(validResult);
Adlai Hollerd71b7b02020-06-08 15:55:00 -0400238 opsTask.addOp(drawingMgr, std::move(op),
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400239 GrTextureResolveManager(dContext->priv().drawingManager()),
240 *caps);
Brian Salomon588cec72018-11-14 13:56:37 -0500241 }
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400242 opsTask.makeClosed(*caps);
Greg Danielf41b2bd2019-08-22 16:19:24 -0400243 opsTask.prepare(&flushState);
244 opsTask.execute(&flushState);
Adlai Hollerd71b7b02020-06-08 15:55:00 -0400245 opsTask.endFlush(drawingMgr);
246 opsTask.disown(drawingMgr);
Brian Salomona7682c82018-10-24 10:04:37 -0400247#if 0 // Useful to repeat a random configuration that fails the test while debugger attached.
Brian Salomon588cec72018-11-14 13:56:37 -0500248 if (!std::equal(result, result + result_width(), validResult)) {
249 repeat = true;
250 }
Brian Salomona7682c82018-10-24 10:04:37 -0400251#endif
Brian Salomon588cec72018-11-14 13:56:37 -0500252 (void)repeat;
253 REPORTER_ASSERT(reporter, std::equal(result, result + result_width(), validResult));
254 }
Brian Salomon764e5462018-08-21 12:07:00 -0400255 }
Brian Salomona7682c82018-10-24 10:04:37 -0400256 }
Brian Salomon764e5462018-08-21 12:07:00 -0400257}