blob: 0bd1e560655ba8ad862b18b2528d69246fb40ff5 [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
8#include "GrContext.h"
9#include "GrContextPriv.h"
10#include "GrMemoryPool.h"
11#include "GrOpFlushState.h"
12#include "GrRenderTargetOpList.h"
13#include "Test.h"
14#include "ops/GrOp.h"
15
16// The number of configurations is 2^Permutations(kNumOps, 2) * kNumOps!. To make this any larger
17// we'd have to probabilistically sample the space. At four this finishes in a very reasonable
18// amount of time but at five it takes nearly 2 minutes in a Release build on a Z840. Four seems
19// like it generates sufficiently complex test cases.
20static constexpr int kNumOps = 4;
21
22static constexpr int fact(int n) {
23 assert(n > 0);
24 return n > 1 ? n * fact(n - 1) : 1;
25}
26
27// Number of possible allowable binary chainings among the kNumOps ops.
28static constexpr int kNumChainabilityBits = fact(kNumOps) / fact(kNumOps - 2);
29
30// We store the chainability booleans as a 32 bit bitfield.
31GR_STATIC_ASSERT(kNumChainabilityBits <= 32);
32
33static constexpr uint64_t kNumChainabilityPermutations = 1 << kNumChainabilityBits;
34
35/** Is op a chainable to op b as indicated by the bitfield? */
36static bool is_chainable(int a, int b, uint32_t chainabilityBits) {
37 SkASSERT(b != a);
38 // Each index gets kNumOps - 1 contiguous bits
39 int aOffset = a * (kNumOps - 1);
40 // Within a's range we give one bit each other op, but not one for itself.
41 int bIdxInA = b < a ? b : b - 1;
42 return chainabilityBits & (1 << (aOffset + bIdxInA));
43}
44
45namespace {
46/**
47 * A simple test op. It has an integer position, p. When it executes it writes p into an array
48 * of ints at index p and p+1. It takes a bitfield that indicates allowed pair-wise chainings.
49 */
50class TestOp : public GrOp {
51public:
52 DEFINE_OP_CLASS_ID
53
54 static std::unique_ptr<TestOp> Make(GrContext* context, int pos, int result[],
55 uint32_t chainability) {
56 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
57 return pool->allocate<TestOp>(pos, result, chainability);
58 }
59
60 const char* name() const override { return "TestOp"; }
61
62 void writeResult(int result[]) const { result[fPos + 1] = result[fPos] = fPos; }
63
64private:
65 friend class ::GrOpMemoryPool; // for ctor
66
67 TestOp(int pos, int result[], uint32_t chainability)
68 : INHERITED(ClassID()), fPos(pos), fResult(result), fChainability(chainability) {
69 // Each op writes 2 values (at pos and pos+1) in a (kNumOps + 1) x 1 buffer.
70 this->setBounds(SkRect::MakeXYWH(pos, 0, 2, 1), HasAABloat::kNo, IsZeroArea::kNo);
71 }
72
73 void onPrepare(GrOpFlushState*) override {}
74
75 void onExecute(GrOpFlushState*) override {
76 for (auto& op : ChainRange<TestOp>(this)) {
77 op.writeResult(fResult);
78 }
79 }
80
81 CombineResult onCombineIfPossible(GrOp* that, const GrCaps&) override {
82 return is_chainable(fPos, that->cast<TestOp>()->fPos, fChainability)
83 ? CombineResult::kMayChain
84 : CombineResult::kCannotCombine;
85 }
86
87 int fPos;
88 int* fResult;
89 uint32_t fChainability;
90
91 typedef GrOp INHERITED;
92};
93} // namespace
94
95/**
96 * Tests adding kNumOps to an op list with all possible allowed chaining configurations. Tests
97 * adding the ops in all possible orders and verifies that the chained executions don't violate
98 * painter's order.
99 */
100DEF_GPUTEST(OpChainTest, reporter, /*ctxInfo*/) {
101 auto context = GrContext::MakeMock(nullptr);
102 SkASSERT(context);
103 GrSurfaceDesc desc;
104 desc.fConfig = kRGBA_8888_GrPixelConfig;
105 desc.fWidth = kNumOps + 1;
106 desc.fHeight = 1;
107 desc.fFlags = kRenderTarget_GrSurfaceFlag;
108
109 auto proxy = context->contextPriv().proxyProvider()->createProxy(
110 desc, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, SkBackingFit::kExact, SkBudgeted::kNo,
111 GrInternalSurfaceFlags::kNone);
112 SkASSERT(proxy);
113 proxy->instantiate(context->contextPriv().resourceProvider());
114 int result[kNumOps + 1];
115 int validResult[kNumOps + 1];
116
117 int permutation[kNumOps];
118 for (int i = 0; i < kNumOps; ++i) {
119 permutation[i] = i;
120 }
121 do {
122 for (uint32_t chainabilityBits = 0; chainabilityBits < kNumChainabilityPermutations;
123 ++chainabilityBits) {
124 GrTokenTracker tracker;
125 GrOpFlushState flushState(context->contextPriv().getGpu(),
126 context->contextPriv().resourceProvider(), &tracker);
127 GrRenderTargetOpList opList(context->contextPriv().resourceProvider(),
128 sk_ref_sp(context->contextPriv().opMemoryPool()),
129 proxy->asRenderTargetProxy(),
130 context->contextPriv().getAuditTrail());
131 std::fill(result, result + kNumOps + 1, -1);
132 std::fill(validResult, validResult + kNumOps + 1, -1);
133 for (int i = 0; i < kNumOps; ++i) {
134 auto op = TestOp::Make(context.get(), permutation[i], result, chainabilityBits);
135 op->writeResult(validResult);
136 opList.addOp(std::move(op), *context->contextPriv().caps());
137 }
138 opList.makeClosed(*context->contextPriv().caps());
139 opList.prepare(&flushState);
140 opList.execute(&flushState);
141 opList.endFlush();
142 REPORTER_ASSERT(reporter, std::equal(result, result + kNumOps + 1, validResult));
143 }
144 } while (std::next_permutation(permutation, permutation + kNumOps));
145}