blob: f86eb040371398e053e5952b0e3ba4f0150216aa [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/gpu/GrContext.h"
9#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "src/gpu/ops/GrOp.h"
15#include "tests/Test.h"
Brian Salomon764e5462018-08-21 12:07:00 -040016
Brian Salomona7682c82018-10-24 10:04:37 -040017// We create Ops that write a value into a range of a buffer. We create ranges from
18// kNumOpPositions starting positions x kRanges canonical ranges. We repeat each range kNumRepeats
19// times (with a different value written by each of the repeats).
20namespace {
21struct Range {
22 unsigned fOffset;
23 unsigned fLength;
24};
Brian Salomon764e5462018-08-21 12:07:00 -040025
Brian Salomona7682c82018-10-24 10:04:37 -040026static constexpr int kNumOpPositions = 4;
27static constexpr Range kRanges[] = {{0, 4,}, {1, 2}};
28static constexpr int kNumRanges = (int)SK_ARRAY_COUNT(kRanges);
29static constexpr int kNumRepeats = 2;
30static constexpr int kNumOps = kNumRepeats * kNumOpPositions * kNumRanges;
31
32static constexpr uint64_t fact(int n) {
Brian Salomon764e5462018-08-21 12:07:00 -040033 assert(n > 0);
34 return n > 1 ? n * fact(n - 1) : 1;
35}
36
Brian Salomona7682c82018-10-24 10:04:37 -040037// How wide should our result buffer be to hold values written by the ranges of the ops.
38static constexpr unsigned result_width() {
39 unsigned maxLength = 0;
40 for (size_t i = 0; i < kNumRanges; ++i) {
41 maxLength = maxLength > kRanges[i].fLength ? maxLength : kRanges[i].fLength;
42 }
43 return kNumOpPositions + maxLength - 1;
Brian Salomon764e5462018-08-21 12:07:00 -040044}
45
Brian Salomona7682c82018-10-24 10:04:37 -040046// Number of possible allowable binary chainings among the kNumOps ops.
47static constexpr int kNumCombinableValues = fact(kNumOps) / fact(kNumOps - 2);
48using Combinable = std::array<GrOp::CombineResult, kNumCombinableValues>;
49
Brian Salomon588cec72018-11-14 13:56:37 -050050/**
51 * The index in Combinable for the result for combining op 'b' into op 'a', i.e. the result of
52 * op[a]->combineIfPossible(op[b]).
53 */
54int64_t combinable_index(int a, int b) {
Brian Salomona7682c82018-10-24 10:04:37 -040055 SkASSERT(b != a);
56 // Each index gets kNumOps - 1 contiguous bools
Brian Salomon588cec72018-11-14 13:56:37 -050057 int64_t aOffset = a * (kNumOps - 1);
Brian Salomona7682c82018-10-24 10:04:37 -040058 // Within a's range we have one value each other op, but not one for a itself.
59 int64_t bIdxInA = b < a ? b : b - 1;
Brian Salomon588cec72018-11-14 13:56:37 -050060 return aOffset + bIdxInA;
61}
62
63/**
64 * Creates a legal set of combinability results for the ops. The likelihood that any two ops
65 * in a group can merge is randomly chosen.
66 */
67static void init_combinable(int numGroups, Combinable* combinable, SkRandom* random) {
68 SkScalar mergeProbability = random->nextUScalar1();
69 std::fill_n(combinable->begin(), kNumCombinableValues, GrOp::CombineResult::kCannotCombine);
70 SkTDArray<int> groups[kNumOps];
71 for (int i = 0; i < kNumOps; ++i) {
72 auto& group = groups[random->nextULessThan(numGroups)];
73 for (int g = 0; g < group.count(); ++g) {
74 int j = group[g];
75 if (random->nextUScalar1() < mergeProbability) {
76 (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMerged;
77 } else {
78 (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMayChain;
79 }
80 if (random->nextUScalar1() < mergeProbability) {
81 (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMerged;
82 } else {
83 (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMayChain;
84 }
85 }
86 group.push_back(i);
87 }
Brian Salomona7682c82018-10-24 10:04:37 -040088}
89
Brian Salomon764e5462018-08-21 12:07:00 -040090/**
91 * A simple test op. It has an integer position, p. When it executes it writes p into an array
92 * of ints at index p and p+1. It takes a bitfield that indicates allowed pair-wise chainings.
93 */
94class TestOp : public GrOp {
95public:
96 DEFINE_OP_CLASS_ID
97
Brian Salomona7682c82018-10-24 10:04:37 -040098 static std::unique_ptr<TestOp> Make(GrContext* context, int value, const Range& range,
99 int result[], const Combinable* combinable) {
Robert Phillips9da87e02019-02-04 13:26:26 -0500100 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Brian Salomona7682c82018-10-24 10:04:37 -0400101 return pool->allocate<TestOp>(value, range, result, combinable);
Brian Salomon764e5462018-08-21 12:07:00 -0400102 }
103
104 const char* name() const override { return "TestOp"; }
105
Brian Salomona7682c82018-10-24 10:04:37 -0400106 void writeResult(int result[]) const {
107 for (const auto& op : ChainRange<TestOp>(this)) {
108 for (const auto& vr : op.fValueRanges) {
109 for (unsigned i = 0; i < vr.fRange.fLength; ++i) {
110 result[vr.fRange.fOffset + i] = vr.fValue;
111 }
112 }
113 }
114 }
Brian Salomon764e5462018-08-21 12:07:00 -0400115
116private:
117 friend class ::GrOpMemoryPool; // for ctor
118
Brian Salomona7682c82018-10-24 10:04:37 -0400119 TestOp(int value, const Range& range, int result[], const Combinable* combinable)
120 : INHERITED(ClassID()), fResult(result), fCombinable(combinable) {
121 fValueRanges.push_back({value, range});
122 this->setBounds(SkRect::MakeXYWH(range.fOffset, 0, range.fOffset + range.fLength, 1),
Greg Daniel5faf4742019-10-01 15:14:44 -0400123 HasAABloat::kNo, IsHairline::kNo);
Brian Salomon764e5462018-08-21 12:07:00 -0400124 }
125
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400126 void onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400127 const GrSurfaceProxyView* writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400128 GrAppliedClip*,
129 const GrXferProcessor::DstProxyView&) override {}
130
Brian Salomon764e5462018-08-21 12:07:00 -0400131 void onPrepare(GrOpFlushState*) override {}
132
Brian Salomon588cec72018-11-14 13:56:37 -0500133 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override {
Brian Salomon764e5462018-08-21 12:07:00 -0400134 for (auto& op : ChainRange<TestOp>(this)) {
135 op.writeResult(fResult);
136 }
137 }
138
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500139 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas* arenas,
140 const GrCaps&) override {
141 // This op doesn't use the arenas, but make sure the GrOpsTask is sending it
142 SkASSERT(arenas);
143 (void) arenas;
Brian Salomona7682c82018-10-24 10:04:37 -0400144 auto that = t->cast<TestOp>();
Brian Salomon588cec72018-11-14 13:56:37 -0500145 int v0 = fValueRanges[0].fValue;
146 int v1 = that->fValueRanges[0].fValue;
147 auto result = (*fCombinable)[combinable_index(v0, v1)];
Brian Salomona7682c82018-10-24 10:04:37 -0400148 if (result == GrOp::CombineResult::kMerged) {
149 std::move(that->fValueRanges.begin(), that->fValueRanges.end(),
150 std::back_inserter(fValueRanges));
Brian Salomona7682c82018-10-24 10:04:37 -0400151 }
152 return result;
Brian Salomon764e5462018-08-21 12:07:00 -0400153 }
154
Brian Salomona7682c82018-10-24 10:04:37 -0400155 struct ValueRange {
156 int fValue;
157 Range fRange;
158 };
159 std::vector<ValueRange> fValueRanges;
Brian Salomon764e5462018-08-21 12:07:00 -0400160 int* fResult;
Brian Salomona7682c82018-10-24 10:04:37 -0400161 const Combinable* fCombinable;
Brian Salomon764e5462018-08-21 12:07:00 -0400162
163 typedef GrOp INHERITED;
164};
165} // namespace
166
167/**
168 * Tests adding kNumOps to an op list with all possible allowed chaining configurations. Tests
169 * adding the ops in all possible orders and verifies that the chained executions don't violate
170 * painter's order.
171 */
172DEF_GPUTEST(OpChainTest, reporter, /*ctxInfo*/) {
173 auto context = GrContext::MakeMock(nullptr);
174 SkASSERT(context);
Brian Salomona56a7462020-02-07 14:17:25 -0500175 static constexpr SkISize kDims = {kNumOps + 1, 1};
Brian Salomon764e5462018-08-21 12:07:00 -0400176
Greg Daniel4065d452018-11-16 15:43:41 -0500177 const GrBackendFormat format =
Robert Phillips0a15cc62019-07-30 12:49:10 -0400178 context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
179 GrRenderable::kYes);
Greg Daniel4065d452018-11-16 15:43:41 -0500180
Greg Daniel16f5c652019-10-29 11:26:01 -0400181 static const GrSurfaceOrigin kOrigin = kTopLeft_GrSurfaceOrigin;
Robert Phillips9da87e02019-02-04 13:26:26 -0500182 auto proxy = context->priv().proxyProvider()->createProxy(
Brian Salomondf1bd6d2020-03-26 20:37:01 -0400183 format, kDims, GrRenderable::kYes, 1, GrMipMapped::kNo, SkBackingFit::kExact,
Greg Daniel3a365112020-02-14 10:47:18 -0500184 SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone);
Brian Salomon764e5462018-08-21 12:07:00 -0400185 SkASSERT(proxy);
Robert Phillips9da87e02019-02-04 13:26:26 -0500186 proxy->instantiate(context->priv().resourceProvider());
Greg Daniel16f5c652019-10-29 11:26:01 -0400187
Brian Salomon8afde5f2020-04-01 16:22:00 -0400188 GrSwizzle writeSwizzle = context->priv().caps()->getWriteSwizzle(format, GrColorType::kRGBA_8888);
Greg Daniel16f5c652019-10-29 11:26:01 -0400189
Brian Salomona7682c82018-10-24 10:04:37 -0400190 int result[result_width()];
191 int validResult[result_width()];
Brian Salomon764e5462018-08-21 12:07:00 -0400192
193 int permutation[kNumOps];
194 for (int i = 0; i < kNumOps; ++i) {
195 permutation[i] = i;
196 }
Brian Salomon588cec72018-11-14 13:56:37 -0500197 // Op order permutations.
Brian Salomona7682c82018-10-24 10:04:37 -0400198 static constexpr int kNumPermutations = 100;
Brian Salomon588cec72018-11-14 13:56:37 -0500199 // For a given number of chainability groups, this is the number of random combinability reuslts
200 // we will test.
201 static constexpr int kNumCombinabilitiesPerGrouping = 20;
Brian Salomona7682c82018-10-24 10:04:37 -0400202 SkRandom random;
203 bool repeat = false;
204 Combinable combinable;
205 for (int p = 0; p < kNumPermutations; ++p) {
206 for (int i = 0; i < kNumOps - 2 && !repeat; ++i) {
207 // The current implementation of nextULessThan() is biased. :(
208 unsigned j = i + random.nextULessThan(kNumOps - i);
209 std::swap(permutation[i], permutation[j]);
210 }
Brian Salomon588cec72018-11-14 13:56:37 -0500211 // g is the number of chainable groups that we partition the ops into.
212 for (int g = 1; g < kNumOps; ++g) {
213 for (int c = 0; c < kNumCombinabilitiesPerGrouping; ++c) {
214 init_combinable(g, &combinable, &random);
215 GrTokenTracker tracker;
Robert Phillips9da87e02019-02-04 13:26:26 -0500216 GrOpFlushState flushState(context->priv().getGpu(),
Brian Salomon2c791fc2019-04-02 11:52:03 -0400217 context->priv().resourceProvider(),
Brian Salomon2c791fc2019-04-02 11:52:03 -0400218 &tracker);
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500219 GrOpsTask opsTask(context->priv().arenas(),
Brian Salomon8afde5f2020-04-01 16:22:00 -0400220 GrSurfaceProxyView(proxy, kOrigin, writeSwizzle),
Robert Phillips61fc7992019-10-22 11:58:17 -0400221 context->priv().auditTrail());
Brian Salomon588cec72018-11-14 13:56:37 -0500222 // This assumes the particular values of kRanges.
223 std::fill_n(result, result_width(), -1);
224 std::fill_n(validResult, result_width(), -1);
225 for (int i = 0; i < kNumOps; ++i) {
226 int value = permutation[i];
227 // factor out the repeats and then use the canonical starting position and range
228 // to determine an actual range.
229 int j = value % (kNumRanges * kNumOpPositions);
230 int pos = j % kNumOpPositions;
231 Range range = kRanges[j / kNumOpPositions];
232 range.fOffset += pos;
233 auto op = TestOp::Make(context.get(), value, range, result, &combinable);
234 op->writeResult(validResult);
Greg Danielf41b2bd2019-08-22 16:19:24 -0400235 opsTask.addOp(std::move(op),
236 GrTextureResolveManager(context->priv().drawingManager()),
237 *context->priv().caps());
Brian Salomon588cec72018-11-14 13:56:37 -0500238 }
Greg Danielf41b2bd2019-08-22 16:19:24 -0400239 opsTask.makeClosed(*context->priv().caps());
240 opsTask.prepare(&flushState);
241 opsTask.execute(&flushState);
242 opsTask.endFlush();
Brian Salomona7682c82018-10-24 10:04:37 -0400243#if 0 // Useful to repeat a random configuration that fails the test while debugger attached.
Brian Salomon588cec72018-11-14 13:56:37 -0500244 if (!std::equal(result, result + result_width(), validResult)) {
245 repeat = true;
246 }
Brian Salomona7682c82018-10-24 10:04:37 -0400247#endif
Brian Salomon588cec72018-11-14 13:56:37 -0500248 (void)repeat;
249 REPORTER_ASSERT(reporter, std::equal(result, result + result_width(), validResult));
250 }
Brian Salomon764e5462018-08-21 12:07:00 -0400251 }
Brian Salomona7682c82018-10-24 10:04:37 -0400252 }
Brian Salomon764e5462018-08-21 12:07:00 -0400253}