blob: cdc9b197d0e0974204cc8f09990edf683966c2e0 [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"
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
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400100 static std::unique_ptr<TestOp> Make(GrRecordingContext* context, int value, const Range& range,
Brian Salomona7682c82018-10-24 10:04:37 -0400101 int result[], const Combinable* combinable) {
Robert Phillips9da87e02019-02-04 13:26:26 -0500102 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Brian Salomona7682c82018-10-24 10:04:37 -0400103 return pool->allocate<TestOp>(value, range, result, combinable);
Brian Salomon764e5462018-08-21 12:07:00 -0400104 }
105
106 const char* name() const override { return "TestOp"; }
107
Brian Salomona7682c82018-10-24 10:04:37 -0400108 void writeResult(int result[]) const {
109 for (const auto& op : ChainRange<TestOp>(this)) {
110 for (const auto& vr : op.fValueRanges) {
111 for (unsigned i = 0; i < vr.fRange.fLength; ++i) {
112 result[vr.fRange.fOffset + i] = vr.fValue;
113 }
114 }
115 }
116 }
Brian Salomon764e5462018-08-21 12:07:00 -0400117
118private:
119 friend class ::GrOpMemoryPool; // for ctor
120
Brian Salomona7682c82018-10-24 10:04:37 -0400121 TestOp(int value, const Range& range, int result[], const Combinable* combinable)
122 : INHERITED(ClassID()), fResult(result), fCombinable(combinable) {
123 fValueRanges.push_back({value, range});
124 this->setBounds(SkRect::MakeXYWH(range.fOffset, 0, range.fOffset + range.fLength, 1),
Greg Daniel5faf4742019-10-01 15:14:44 -0400125 HasAABloat::kNo, IsHairline::kNo);
Brian Salomon764e5462018-08-21 12:07:00 -0400126 }
127
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400128 void onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400129 const GrSurfaceProxyView* writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400130 GrAppliedClip*,
131 const GrXferProcessor::DstProxyView&) override {}
132
Brian Salomon764e5462018-08-21 12:07:00 -0400133 void onPrepare(GrOpFlushState*) override {}
134
Brian Salomon588cec72018-11-14 13:56:37 -0500135 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override {
Brian Salomon764e5462018-08-21 12:07:00 -0400136 for (auto& op : ChainRange<TestOp>(this)) {
137 op.writeResult(fResult);
138 }
139 }
140
Michael Ludwig28b0c5d2019-12-19 14:51:00 -0500141 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas* arenas,
142 const GrCaps&) override {
143 // This op doesn't use the arenas, but make sure the GrOpsTask is sending it
144 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
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400190 GrSwizzle 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();
Brian Salomona7682c82018-10-24 10:04:37 -0400208 for (int p = 0; p < kNumPermutations; ++p) {
209 for (int i = 0; i < kNumOps - 2 && !repeat; ++i) {
210 // The current implementation of nextULessThan() is biased. :(
211 unsigned j = i + random.nextULessThan(kNumOps - i);
212 std::swap(permutation[i], permutation[j]);
213 }
Brian Salomon588cec72018-11-14 13:56:37 -0500214 // g is the number of chainable groups that we partition the ops into.
215 for (int g = 1; g < kNumOps; ++g) {
216 for (int c = 0; c < kNumCombinabilitiesPerGrouping; ++c) {
217 init_combinable(g, &combinable, &random);
218 GrTokenTracker tracker;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400219 GrOpFlushState flushState(dContext->priv().getGpu(),
220 dContext->priv().resourceProvider(),
Brian Salomon2c791fc2019-04-02 11:52:03 -0400221 &tracker);
Adlai Hollerd71b7b02020-06-08 15:55:00 -0400222 GrOpsTask opsTask(drawingMgr,
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400223 dContext->priv().arenas(),
Brian Salomon8afde5f2020-04-01 16:22:00 -0400224 GrSurfaceProxyView(proxy, kOrigin, writeSwizzle),
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400225 dContext->priv().auditTrail());
Brian Salomon588cec72018-11-14 13:56:37 -0500226 // This assumes the particular values of kRanges.
227 std::fill_n(result, result_width(), -1);
228 std::fill_n(validResult, result_width(), -1);
229 for (int i = 0; i < kNumOps; ++i) {
230 int value = permutation[i];
231 // factor out the repeats and then use the canonical starting position and range
232 // to determine an actual range.
233 int j = value % (kNumRanges * kNumOpPositions);
234 int pos = j % kNumOpPositions;
235 Range range = kRanges[j / kNumOpPositions];
236 range.fOffset += pos;
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400237 auto op = TestOp::Make(dContext.get(), value, range, result, &combinable);
Brian Salomon588cec72018-11-14 13:56:37 -0500238 op->writeResult(validResult);
Adlai Hollerd71b7b02020-06-08 15:55:00 -0400239 opsTask.addOp(drawingMgr, std::move(op),
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400240 GrTextureResolveManager(dContext->priv().drawingManager()),
241 *caps);
Brian Salomon588cec72018-11-14 13:56:37 -0500242 }
Robert Phillips0c5bb2f2020-07-17 15:40:13 -0400243 opsTask.makeClosed(*caps);
Greg Danielf41b2bd2019-08-22 16:19:24 -0400244 opsTask.prepare(&flushState);
245 opsTask.execute(&flushState);
Adlai Hollerd71b7b02020-06-08 15:55:00 -0400246 opsTask.endFlush(drawingMgr);
247 opsTask.disown(drawingMgr);
Brian Salomona7682c82018-10-24 10:04:37 -0400248#if 0 // Useful to repeat a random configuration that fails the test while debugger attached.
Brian Salomon588cec72018-11-14 13:56:37 -0500249 if (!std::equal(result, result + result_width(), validResult)) {
250 repeat = true;
251 }
Brian Salomona7682c82018-10-24 10:04:37 -0400252#endif
Brian Salomon588cec72018-11-14 13:56:37 -0500253 (void)repeat;
254 REPORTER_ASSERT(reporter, std::equal(result, result + result_width(), validResult));
255 }
Brian Salomon764e5462018-08-21 12:07:00 -0400256 }
Brian Salomona7682c82018-10-24 10:04:37 -0400257 }
Brian Salomon764e5462018-08-21 12:07:00 -0400258}