OpChainTest.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /*
  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. #include "include/gpu/GrContext.h"
  8. #include "src/gpu/GrContextPriv.h"
  9. #include "src/gpu/GrMemoryPool.h"
  10. #include "src/gpu/GrOpFlushState.h"
  11. #include "src/gpu/GrRenderTargetOpList.h"
  12. #include "src/gpu/ops/GrOp.h"
  13. #include "tests/Test.h"
  14. // We create Ops that write a value into a range of a buffer. We create ranges from
  15. // kNumOpPositions starting positions x kRanges canonical ranges. We repeat each range kNumRepeats
  16. // times (with a different value written by each of the repeats).
  17. namespace {
  18. struct Range {
  19. unsigned fOffset;
  20. unsigned fLength;
  21. };
  22. static constexpr int kNumOpPositions = 4;
  23. static constexpr Range kRanges[] = {{0, 4,}, {1, 2}};
  24. static constexpr int kNumRanges = (int)SK_ARRAY_COUNT(kRanges);
  25. static constexpr int kNumRepeats = 2;
  26. static constexpr int kNumOps = kNumRepeats * kNumOpPositions * kNumRanges;
  27. static constexpr uint64_t fact(int n) {
  28. assert(n > 0);
  29. return n > 1 ? n * fact(n - 1) : 1;
  30. }
  31. // How wide should our result buffer be to hold values written by the ranges of the ops.
  32. static constexpr unsigned result_width() {
  33. unsigned maxLength = 0;
  34. for (size_t i = 0; i < kNumRanges; ++i) {
  35. maxLength = maxLength > kRanges[i].fLength ? maxLength : kRanges[i].fLength;
  36. }
  37. return kNumOpPositions + maxLength - 1;
  38. }
  39. // Number of possible allowable binary chainings among the kNumOps ops.
  40. static constexpr int kNumCombinableValues = fact(kNumOps) / fact(kNumOps - 2);
  41. using Combinable = std::array<GrOp::CombineResult, kNumCombinableValues>;
  42. /**
  43. * The index in Combinable for the result for combining op 'b' into op 'a', i.e. the result of
  44. * op[a]->combineIfPossible(op[b]).
  45. */
  46. int64_t combinable_index(int a, int b) {
  47. SkASSERT(b != a);
  48. // Each index gets kNumOps - 1 contiguous bools
  49. int64_t aOffset = a * (kNumOps - 1);
  50. // Within a's range we have one value each other op, but not one for a itself.
  51. int64_t bIdxInA = b < a ? b : b - 1;
  52. return aOffset + bIdxInA;
  53. }
  54. /**
  55. * Creates a legal set of combinability results for the ops. The likelihood that any two ops
  56. * in a group can merge is randomly chosen.
  57. */
  58. static void init_combinable(int numGroups, Combinable* combinable, SkRandom* random) {
  59. SkScalar mergeProbability = random->nextUScalar1();
  60. std::fill_n(combinable->begin(), kNumCombinableValues, GrOp::CombineResult::kCannotCombine);
  61. SkTDArray<int> groups[kNumOps];
  62. for (int i = 0; i < kNumOps; ++i) {
  63. auto& group = groups[random->nextULessThan(numGroups)];
  64. for (int g = 0; g < group.count(); ++g) {
  65. int j = group[g];
  66. if (random->nextUScalar1() < mergeProbability) {
  67. (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMerged;
  68. } else {
  69. (*combinable)[combinable_index(i, j)] = GrOp::CombineResult::kMayChain;
  70. }
  71. if (random->nextUScalar1() < mergeProbability) {
  72. (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMerged;
  73. } else {
  74. (*combinable)[combinable_index(j, i)] = GrOp::CombineResult::kMayChain;
  75. }
  76. }
  77. group.push_back(i);
  78. }
  79. }
  80. /**
  81. * A simple test op. It has an integer position, p. When it executes it writes p into an array
  82. * of ints at index p and p+1. It takes a bitfield that indicates allowed pair-wise chainings.
  83. */
  84. class TestOp : public GrOp {
  85. public:
  86. DEFINE_OP_CLASS_ID
  87. static std::unique_ptr<TestOp> Make(GrContext* context, int value, const Range& range,
  88. int result[], const Combinable* combinable) {
  89. GrOpMemoryPool* pool = context->priv().opMemoryPool();
  90. return pool->allocate<TestOp>(value, range, result, combinable);
  91. }
  92. const char* name() const override { return "TestOp"; }
  93. void writeResult(int result[]) const {
  94. for (const auto& op : ChainRange<TestOp>(this)) {
  95. for (const auto& vr : op.fValueRanges) {
  96. for (unsigned i = 0; i < vr.fRange.fLength; ++i) {
  97. result[vr.fRange.fOffset + i] = vr.fValue;
  98. }
  99. }
  100. }
  101. }
  102. private:
  103. friend class ::GrOpMemoryPool; // for ctor
  104. TestOp(int value, const Range& range, int result[], const Combinable* combinable)
  105. : INHERITED(ClassID()), fResult(result), fCombinable(combinable) {
  106. fValueRanges.push_back({value, range});
  107. this->setBounds(SkRect::MakeXYWH(range.fOffset, 0, range.fOffset + range.fLength, 1),
  108. HasAABloat::kNo, IsZeroArea::kNo);
  109. }
  110. void onPrepare(GrOpFlushState*) override {}
  111. void onExecute(GrOpFlushState*, const SkRect& chainBounds) override {
  112. for (auto& op : ChainRange<TestOp>(this)) {
  113. op.writeResult(fResult);
  114. }
  115. }
  116. CombineResult onCombineIfPossible(GrOp* t, const GrCaps&) override {
  117. auto that = t->cast<TestOp>();
  118. int v0 = fValueRanges[0].fValue;
  119. int v1 = that->fValueRanges[0].fValue;
  120. auto result = (*fCombinable)[combinable_index(v0, v1)];
  121. if (result == GrOp::CombineResult::kMerged) {
  122. std::move(that->fValueRanges.begin(), that->fValueRanges.end(),
  123. std::back_inserter(fValueRanges));
  124. }
  125. return result;
  126. }
  127. struct ValueRange {
  128. int fValue;
  129. Range fRange;
  130. };
  131. std::vector<ValueRange> fValueRanges;
  132. int* fResult;
  133. const Combinable* fCombinable;
  134. typedef GrOp INHERITED;
  135. };
  136. } // namespace
  137. /**
  138. * Tests adding kNumOps to an op list with all possible allowed chaining configurations. Tests
  139. * adding the ops in all possible orders and verifies that the chained executions don't violate
  140. * painter's order.
  141. */
  142. DEF_GPUTEST(OpChainTest, reporter, /*ctxInfo*/) {
  143. auto context = GrContext::MakeMock(nullptr);
  144. SkASSERT(context);
  145. GrSurfaceDesc desc;
  146. desc.fConfig = kRGBA_8888_GrPixelConfig;
  147. desc.fWidth = kNumOps + 1;
  148. desc.fHeight = 1;
  149. const GrBackendFormat format =
  150. context->priv().caps()->getBackendFormatFromColorType(GrColorType::kRGBA_8888);
  151. auto proxy = context->priv().proxyProvider()->createProxy(
  152. format, desc, GrRenderable::kYes, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
  153. SkBackingFit::kExact, SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone);
  154. SkASSERT(proxy);
  155. proxy->instantiate(context->priv().resourceProvider());
  156. int result[result_width()];
  157. int validResult[result_width()];
  158. int permutation[kNumOps];
  159. for (int i = 0; i < kNumOps; ++i) {
  160. permutation[i] = i;
  161. }
  162. // Op order permutations.
  163. static constexpr int kNumPermutations = 100;
  164. // For a given number of chainability groups, this is the number of random combinability reuslts
  165. // we will test.
  166. static constexpr int kNumCombinabilitiesPerGrouping = 20;
  167. SkRandom random;
  168. bool repeat = false;
  169. Combinable combinable;
  170. for (int p = 0; p < kNumPermutations; ++p) {
  171. for (int i = 0; i < kNumOps - 2 && !repeat; ++i) {
  172. // The current implementation of nextULessThan() is biased. :(
  173. unsigned j = i + random.nextULessThan(kNumOps - i);
  174. std::swap(permutation[i], permutation[j]);
  175. }
  176. // g is the number of chainable groups that we partition the ops into.
  177. for (int g = 1; g < kNumOps; ++g) {
  178. for (int c = 0; c < kNumCombinabilitiesPerGrouping; ++c) {
  179. init_combinable(g, &combinable, &random);
  180. GrTokenTracker tracker;
  181. GrOpFlushState flushState(context->priv().getGpu(),
  182. context->priv().resourceProvider(),
  183. &tracker);
  184. GrRenderTargetOpList opList(sk_ref_sp(context->priv().opMemoryPool()),
  185. sk_ref_sp(proxy->asRenderTargetProxy()),
  186. context->priv().auditTrail());
  187. // This assumes the particular values of kRanges.
  188. std::fill_n(result, result_width(), -1);
  189. std::fill_n(validResult, result_width(), -1);
  190. for (int i = 0; i < kNumOps; ++i) {
  191. int value = permutation[i];
  192. // factor out the repeats and then use the canonical starting position and range
  193. // to determine an actual range.
  194. int j = value % (kNumRanges * kNumOpPositions);
  195. int pos = j % kNumOpPositions;
  196. Range range = kRanges[j / kNumOpPositions];
  197. range.fOffset += pos;
  198. auto op = TestOp::Make(context.get(), value, range, result, &combinable);
  199. op->writeResult(validResult);
  200. opList.addOp(std::move(op), *context->priv().caps());
  201. }
  202. opList.makeClosed(*context->priv().caps());
  203. opList.prepare(&flushState);
  204. opList.execute(&flushState);
  205. opList.endFlush();
  206. #if 0 // Useful to repeat a random configuration that fails the test while debugger attached.
  207. if (!std::equal(result, result + result_width(), validResult)) {
  208. repeat = true;
  209. }
  210. #endif
  211. (void)repeat;
  212. REPORTER_ASSERT(reporter, std::equal(result, result + result_width(), validResult));
  213. }
  214. }
  215. }
  216. }