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