blob: 521f13ff41e104c57fec65abe7d71752edec5239 [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"
Adlai Hollera0693042020-10-14 11:23:11 -04009#include "src/gpu/GrDirectContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/gpu/GrMemoryPool.h"
11#include "src/gpu/GrOpFlushState.h"
Robert Phillipse19babf2020-04-06 13:57:30 -040012#include "src/gpu/GrProxyProvider.h"
Robert Phillips0c5bb2f2020-07-17 15:40:13 -040013#include "src/gpu/GrRecordingContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "src/gpu/ops/GrOp.h"
Robert Phillips3e87a8e2021-08-25 13:22:24 -040015#include "src/gpu/ops/OpsTask.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "tests/Test.h"
Mike Klein0d6f8152020-08-06 07:58:25 -050017#include <iterator>
Brian Salomon764e5462018-08-21 12:07:00 -040018
Brian Salomona7682c82018-10-24 10:04:37 -040019// We create Ops that write a value into a range of a buffer. We create ranges from
20// kNumOpPositions starting positions x kRanges canonical ranges. We repeat each range kNumRepeats
21// times (with a different value written by each of the repeats).
22namespace {
23struct Range {
24 unsigned fOffset;
25 unsigned fLength;
26};
Brian Salomon764e5462018-08-21 12:07:00 -040027
Brian Salomona7682c82018-10-24 10:04:37 -040028static constexpr int kNumOpPositions = 4;
29static constexpr Range kRanges[] = {{0, 4,}, {1, 2}};
30static constexpr int kNumRanges = (int)SK_ARRAY_COUNT(kRanges);
31static constexpr int kNumRepeats = 2;
32static constexpr int kNumOps = kNumRepeats * kNumOpPositions * kNumRanges;
33
34static constexpr uint64_t fact(int n) {
Brian Salomon764e5462018-08-21 12:07:00 -040035 assert(n > 0);
36 return n > 1 ? n * fact(n - 1) : 1;
37}
38
Brian Salomona7682c82018-10-24 10:04:37 -040039// How wide should our result buffer be to hold values written by the ranges of the ops.
40static constexpr unsigned result_width() {
41 unsigned maxLength = 0;
42 for (size_t i = 0; i < kNumRanges; ++i) {
43 maxLength = maxLength > kRanges[i].fLength ? maxLength : kRanges[i].fLength;
44 }
45 return kNumOpPositions + maxLength - 1;
Brian Salomon764e5462018-08-21 12:07:00 -040046}
47
Brian Salomona7682c82018-10-24 10:04:37 -040048// Number of possible allowable binary chainings among the kNumOps ops.
49static constexpr int kNumCombinableValues = fact(kNumOps) / fact(kNumOps - 2);
50using Combinable = std::array<GrOp::CombineResult, kNumCombinableValues>;
51
Brian Salomon588cec72018-11-14 13:56:37 -050052/**
53 * The index in Combinable for the result for combining op 'b' into op 'a', i.e. the result of
54 * op[a]->combineIfPossible(op[b]).
55 */
56int64_t combinable_index(int a, int b) {
Brian Salomona7682c82018-10-24 10:04:37 -040057 SkASSERT(b != a);
58 // Each index gets kNumOps - 1 contiguous bools
Brian Salomon588cec72018-11-14 13:56:37 -050059 int64_t aOffset = a * (kNumOps - 1);
Brian Salomona7682c82018-10-24 10:04:37 -040060 // Within a's range we have one value each other op, but not one for a itself.
61 int64_t bIdxInA = b < a ? b : b - 1;
Brian Salomon588cec72018-11-14 13:56:37 -050062 return aOffset + bIdxInA;
63}
64
65/**
66 * Creates a legal set of combinability results for the ops. The likelihood that any two ops
67 * in a group can merge is randomly chosen.
68 */
69static void init_combinable(int numGroups, Combinable* combinable, SkRandom* random) {
70 SkScalar mergeProbability = random->nextUScalar1();
71 std::fill_n(combinable->begin(), kNumCombinableValues, GrOp::CombineResult::kCannotCombine);
72 SkTDArray<int> groups[kNumOps];
73 for (int i = 0; i < kNumOps; ++i) {
74 auto& group = groups[random->nextULessThan(numGroups)];
75 for (int g = 0; g < group.count(); ++g) {
76 int j = group[g];
77 if (random->nextUScalar1() < mergeProbability) {
78 (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMerged;
79 } else {
80 (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMayChain;
81 }
82 if (random->nextUScalar1() < mergeProbability) {
83 (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMerged;
84 } else {
85 (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMayChain;
86 }
87 }
88 group.push_back(i);
89 }
Brian Salomona7682c82018-10-24 10:04:37 -040090}
91
Brian Salomon764e5462018-08-21 12:07:00 -040092/**
93 * A simple test op. It has an integer position, p. When it executes it writes p into an array
94 * of ints at index p and p+1. It takes a bitfield that indicates allowed pair-wise chainings.
95 */
96class TestOp : public GrOp {
97public:
98 DEFINE_OP_CLASS_ID
99
Herb Derbyc76d4092020-10-07 16:46:15 -0400100 static GrOp::Owner Make(GrRecordingContext* context, int value, const Range& range,
101 int result[], const Combinable* combinable) {
102 return GrOp::Make<TestOp>(context, 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:
Herb Derbyc76d4092020-10-07 16:46:15 -0400118 friend class ::GrOp; // for ctor
Brian Salomon764e5462018-08-21 12:07:00 -0400119
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*,
Adlai Hollere2296f72020-11-19 13:41:26 -0500128 const GrSurfaceProxyView& writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400129 GrAppliedClip*,
John Stiles52cb1d02021-06-02 11:58:05 -0400130 const GrDstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500131 GrXferBarrierFlags renderPassXferBarriers,
132 GrLoadOp colorLoadOp) override {}
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400133
Brian Salomon764e5462018-08-21 12:07:00 -0400134 void onPrepare(GrOpFlushState*) override {}
135
Brian Salomon588cec72018-11-14 13:56:37 -0500136 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override {
Brian Salomon764e5462018-08-21 12:07:00 -0400137 for (auto& op : ChainRange<TestOp>(this)) {
138 op.writeResult(fResult);
139 }
140 }
141
Herb Derbye25c3002020-10-27 15:57:27 -0400142 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc* arenas, const GrCaps&) override {
Robert Phillips3e87a8e2021-08-25 13:22:24 -0400143 // This op doesn't use the arenas, but make sure the OpsTask is sending it
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500144 SkASSERT(arenas);
145 (void) arenas;
Brian Salomona7682c82018-10-24 10:04:37 -0400146 auto that = t->cast<TestOp>();
Brian Salomon588cec72018-11-14 13:56:37 -0500147 int v0 = fValueRanges[0].fValue;
148 int v1 = that->fValueRanges[0].fValue;
149 auto result = (*fCombinable)[combinable_index(v0, v1)];
Brian Salomona7682c82018-10-24 10:04:37 -0400150 if (result == GrOp::CombineResult::kMerged) {
151 std::move(that->fValueRanges.begin(), that->fValueRanges.end(),
152 std::back_inserter(fValueRanges));
Brian Salomona7682c82018-10-24 10:04:37 -0400153 }
154 return result;
Brian Salomon764e5462018-08-21 12:07:00 -0400155 }
156
Brian Salomona7682c82018-10-24 10:04:37 -0400157 struct ValueRange {
158 int fValue;
159 Range fRange;
160 };
161 std::vector<ValueRange> fValueRanges;
Brian Salomon764e5462018-08-21 12:07:00 -0400162 int* fResult;
Brian Salomona7682c82018-10-24 10:04:37 -0400163 const Combinable* fCombinable;
Brian Salomon764e5462018-08-21 12:07:00 -0400164
John Stiles7571f9e2020-09-02 22:42:33 -0400165 using INHERITED = GrOp;
Brian Salomon764e5462018-08-21 12:07:00 -0400166};
167} // namespace
168
169/**
170 * Tests adding kNumOps to an op list with all possible allowed chaining configurations. Tests
171 * adding the ops in all possible orders and verifies that the chained executions don't violate
172 * painter's order.
173 */
174DEF_GPUTEST(OpChainTest, reporter, /*ctxInfo*/) {
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400175 sk_sp<GrDirectContext> dContext = GrDirectContext::MakeMock(nullptr);
176 SkASSERT(dContext);
177 const GrCaps* caps = dContext->priv().caps();
Brian Salomona56a7462020-02-07 14:17:25 -0500178 static constexpr SkISize kDims = {kNumOps + 1, 1};
Brian Salomon764e5462018-08-21 12:07:00 -0400179
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400180 const GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
181 GrRenderable::kYes);
Greg Daniel4065d452018-11-16 15:43:41 -0500182
Greg Daniel16f5c652019-10-29 11:26:01 -0400183 static const GrSurfaceOrigin kOrigin = kTopLeft_GrSurfaceOrigin;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400184 auto proxy = dContext->priv().proxyProvider()->createProxy(
Brian Salomon7e67dca2020-07-21 09:27:25 -0400185 format, kDims, GrRenderable::kYes, 1, GrMipmapped::kNo, SkBackingFit::kExact,
Greg Daniel3a365112020-02-14 10:47:18 -0500186 SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone);
Brian Salomon764e5462018-08-21 12:07:00 -0400187 SkASSERT(proxy);
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400188 proxy->instantiate(dContext->priv().resourceProvider());
Greg Daniel16f5c652019-10-29 11:26:01 -0400189
Jim Van Verthd6245fc2022-02-15 16:30:59 -0500190 skgpu::Swizzle writeSwizzle = caps->getWriteSwizzle(format, GrColorType::kRGBA_8888);
Greg Daniel16f5c652019-10-29 11:26:01 -0400191
Brian Salomona7682c82018-10-24 10:04:37 -0400192 int result[result_width()];
193 int validResult[result_width()];
Brian Salomon764e5462018-08-21 12:07:00 -0400194
195 int permutation[kNumOps];
196 for (int i = 0; i < kNumOps; ++i) {
197 permutation[i] = i;
198 }
Brian Salomon588cec72018-11-14 13:56:37 -0500199 // Op order permutations.
Brian Salomona7682c82018-10-24 10:04:37 -0400200 static constexpr int kNumPermutations = 100;
Brian Salomon588cec72018-11-14 13:56:37 -0500201 // For a given number of chainability groups, this is the number of random combinability reuslts
202 // we will test.
203 static constexpr int kNumCombinabilitiesPerGrouping = 20;
Brian Salomona7682c82018-10-24 10:04:37 -0400204 SkRandom random;
205 bool repeat = false;
206 Combinable combinable;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400207 GrDrawingManager* drawingMgr = dContext->priv().drawingManager();
Herb Derby0b1228d2021-04-05 18:38:35 -0400208 sk_sp<GrArenas> arenas = sk_make_sp<GrArenas>();
Brian Salomona7682c82018-10-24 10:04:37 -0400209 for (int p = 0; p < kNumPermutations; ++p) {
210 for (int i = 0; i < kNumOps - 2 && !repeat; ++i) {
211 // The current implementation of nextULessThan() is biased. :(
212 unsigned j = i + random.nextULessThan(kNumOps - i);
213 std::swap(permutation[i], permutation[j]);
214 }
Brian Salomon588cec72018-11-14 13:56:37 -0500215 // g is the number of chainable groups that we partition the ops into.
216 for (int g = 1; g < kNumOps; ++g) {
217 for (int c = 0; c < kNumCombinabilitiesPerGrouping; ++c) {
218 init_combinable(g, &combinable, &random);
219 GrTokenTracker tracker;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400220 GrOpFlushState flushState(dContext->priv().getGpu(),
221 dContext->priv().resourceProvider(),
Brian Salomon2c791fc2019-04-02 11:52:03 -0400222 &tracker);
Robert Phillips3e87a8e2021-08-25 13:22:24 -0400223 skgpu::v1::OpsTask opsTask(drawingMgr,
224 GrSurfaceProxyView(proxy, kOrigin, writeSwizzle),
225 dContext->priv().auditTrail(),
226 arenas);
Brian Salomon588cec72018-11-14 13:56:37 -0500227 // This assumes the particular values of kRanges.
228 std::fill_n(result, result_width(), -1);
229 std::fill_n(validResult, result_width(), -1);
230 for (int i = 0; i < kNumOps; ++i) {
231 int value = permutation[i];
232 // factor out the repeats and then use the canonical starting position and range
233 // to determine an actual range.
234 int j = value % (kNumRanges * kNumOpPositions);
235 int pos = j % kNumOpPositions;
236 Range range = kRanges[j / kNumOpPositions];
237 range.fOffset += pos;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400238 auto op = TestOp::Make(dContext.get(), value, range, result, &combinable);
Herb Derbyc76d4092020-10-07 16:46:15 -0400239 TestOp* testOp = (TestOp*)op.get();
240 testOp->writeResult(validResult);
Adlai Hollerd71b7b02020-06-08 15:55:00 -0400241 opsTask.addOp(drawingMgr, std::move(op),
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400242 GrTextureResolveManager(dContext->priv().drawingManager()),
243 *caps);
Brian Salomon588cec72018-11-14 13:56:37 -0500244 }
Chris Daltonaa938ce2021-06-23 18:13:59 -0600245 opsTask.makeClosed(dContext.get());
Greg Danielf41b2bd2019-08-22 16:19:24 -0400246 opsTask.prepare(&flushState);
247 opsTask.execute(&flushState);
Adlai Hollerd71b7b02020-06-08 15:55:00 -0400248 opsTask.endFlush(drawingMgr);
249 opsTask.disown(drawingMgr);
Brian Salomona7682c82018-10-24 10:04:37 -0400250#if 0 // Useful to repeat a random configuration that fails the test while debugger attached.
Brian Salomon588cec72018-11-14 13:56:37 -0500251 if (!std::equal(result, result + result_width(), validResult)) {
252 repeat = true;
253 }
Brian Salomona7682c82018-10-24 10:04:37 -0400254#endif
Brian Salomon588cec72018-11-14 13:56:37 -0500255 (void)repeat;
256 REPORTER_ASSERT(reporter, std::equal(result, result + result_width(), validResult));
257 }
Brian Salomon764e5462018-08-21 12:07:00 -0400258 }
Brian Salomona7682c82018-10-24 10:04:37 -0400259 }
Brian Salomon764e5462018-08-21 12:07:00 -0400260}