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 "SkTypes.h"
      9 
     10 #include "GrContext.h"
     11 #include "GrContextFactory.h"
     12 #include "GrContextPriv.h"
     13 #include "GrDeferredUpload.h"
     14 #include "GrDrawOpAtlas.h"
     15 #include "GrDrawingManager.h"
     16 #include "GrMemoryPool.h"
     17 #include "GrOnFlushResourceProvider.h"
     18 #include "GrOpFlushState.h"
     19 #include "GrRenderTargetContext.h"
     20 #include "GrSurfaceProxyPriv.h"
     21 #include "GrTextureProxy.h"
     22 #include "GrTypesPriv.h"
     23 #include "GrXferProcessor.h"
     24 #include "SkBitmap.h"
     25 #include "SkColor.h"
     26 #include "SkColorSpace.h"
     27 #include "SkIPoint16.h"
     28 #include "SkImageInfo.h"
     29 #include "SkMatrix.h"
     30 #include "SkPaint.h"
     31 #include "SkPoint.h"
     32 #include "SkRefCnt.h"
     33 #include "Test.h"
     34 #include "ops/GrDrawOp.h"
     35 #include "text/GrAtlasManager.h"
     36 #include "text/GrTextContext.h"
     37 #include "text/GrTextTarget.h"
     38 
     39 #include <memory>
     40 
     41 class GrResourceProvider;
     42 
     43 static const int kNumPlots = 2;
     44 static const int kPlotSize = 32;
     45 static const int kAtlasSize = kNumPlots * kPlotSize;
     46 
     47 int GrDrawOpAtlas::numAllocated_TestingOnly() const {
     48     int count = 0;
     49     for (uint32_t i = 0; i < this->maxPages(); ++i) {
     50         if (fProxies[i]->isInstantiated()) {
     51             ++count;
     52         }
     53     }
     54 
     55     return count;
     56 }
     57 
     58 void GrAtlasManager::setMaxPages_TestingOnly(uint32_t maxPages) {
     59     for (int i = 0; i < kMaskFormatCount; i++) {
     60         if (fAtlases[i]) {
     61             fAtlases[i]->setMaxPages_TestingOnly(maxPages);
     62         }
     63     }
     64 }
     65 
     66 void GrDrawOpAtlas::setMaxPages_TestingOnly(uint32_t maxPages) {
     67     SkASSERT(!fNumActivePages);
     68 
     69     fMaxPages = maxPages;
     70 }
     71 
     72 void EvictionFunc(GrDrawOpAtlas::AtlasID atlasID, void*) {
     73     SkASSERT(0); // The unit test shouldn't exercise this code path
     74 }
     75 
     76 static void check(skiatest::Reporter* r, GrDrawOpAtlas* atlas,
     77                   uint32_t expectedActive, uint32_t expectedMax, int expectedAlloced) {
     78     REPORTER_ASSERT(r, expectedActive == atlas->numActivePages());
     79     REPORTER_ASSERT(r, expectedMax == atlas->maxPages());
     80     REPORTER_ASSERT(r, expectedAlloced == atlas->numAllocated_TestingOnly());
     81 }
     82 
     83 class TestingUploadTarget : public GrDeferredUploadTarget {
     84 public:
     85     TestingUploadTarget() { }
     86 
     87     const GrTokenTracker* tokenTracker() final { return &fTokenTracker; }
     88     GrTokenTracker* writeableTokenTracker() { return &fTokenTracker; }
     89 
     90     GrDeferredUploadToken addInlineUpload(GrDeferredTextureUploadFn&&) final {
     91         SkASSERT(0); // this test shouldn't invoke this code path
     92         return fTokenTracker.nextDrawToken();
     93     }
     94 
     95     virtual GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&& upload) final {
     96         return fTokenTracker.nextTokenToFlush();
     97     }
     98 
     99     void issueDrawToken() { fTokenTracker.issueDrawToken(); }
    100     void flushToken() { fTokenTracker.flushToken(); }
    101 
    102 private:
    103     GrTokenTracker fTokenTracker;
    104 
    105     typedef GrDeferredUploadTarget INHERITED;
    106 };
    107 
    108 static bool fill_plot(GrDrawOpAtlas* atlas,
    109                       GrResourceProvider* resourceProvider,
    110                       GrDeferredUploadTarget* target,
    111                       GrDrawOpAtlas::AtlasID* atlasID,
    112                       int alpha) {
    113     SkImageInfo ii = SkImageInfo::MakeA8(kPlotSize, kPlotSize);
    114 
    115     SkBitmap data;
    116     data.allocPixels(ii);
    117     data.eraseARGB(alpha, 0, 0, 0);
    118 
    119     SkIPoint16 loc;
    120     GrDrawOpAtlas::ErrorCode code;
    121     code = atlas->addToAtlas(resourceProvider, atlasID, target, kPlotSize, kPlotSize,
    122                               data.getAddr(0, 0), &loc);
    123     return GrDrawOpAtlas::ErrorCode::kSucceeded == code;
    124 }
    125 
    126 
    127 // This is a basic DrawOpAtlas test. It simply verifies that multitexture atlases correctly
    128 // add and remove pages. Note that this is simulating flush-time behavior.
    129 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BasicDrawOpAtlas, reporter, ctxInfo) {
    130     auto context = ctxInfo.grContext();
    131     auto proxyProvider = context->priv().proxyProvider();
    132     auto resourceProvider = context->priv().resourceProvider();
    133     auto drawingManager = context->priv().drawingManager();
    134 
    135     GrOnFlushResourceProvider onFlushResourceProvider(drawingManager);
    136     TestingUploadTarget uploadTarget;
    137 
    138     GrBackendFormat format =
    139             context->priv().caps()->getBackendFormatFromColorType(kAlpha_8_SkColorType);
    140 
    141     std::unique_ptr<GrDrawOpAtlas> atlas = GrDrawOpAtlas::Make(
    142                                                 proxyProvider,
    143                                                 format,
    144                                                 kAlpha_8_GrPixelConfig,
    145                                                 kAtlasSize, kAtlasSize,
    146                                                 kAtlasSize/kNumPlots, kAtlasSize/kNumPlots,
    147                                                 GrDrawOpAtlas::AllowMultitexturing::kYes,
    148                                                 EvictionFunc, nullptr);
    149     check(reporter, atlas.get(), 0, 4, 0);
    150 
    151     // Fill up the first level
    152     GrDrawOpAtlas::AtlasID atlasIDs[kNumPlots * kNumPlots];
    153     for (int i = 0; i < kNumPlots * kNumPlots; ++i) {
    154         bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasIDs[i], i*32);
    155         REPORTER_ASSERT(reporter, result);
    156         check(reporter, atlas.get(), 1, 4, 1);
    157     }
    158 
    159     atlas->instantiate(&onFlushResourceProvider);
    160     check(reporter, atlas.get(), 1, 4, 1);
    161 
    162     // Force allocation of a second level
    163     GrDrawOpAtlas::AtlasID atlasID;
    164     bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasID, 4*32);
    165     REPORTER_ASSERT(reporter, result);
    166     check(reporter, atlas.get(), 2, 4, 2);
    167 
    168     // Simulate a lot of draws using only the first plot. The last texture should be compacted.
    169     for (int i = 0; i < 512; ++i) {
    170         atlas->setLastUseToken(atlasIDs[0], uploadTarget.tokenTracker()->nextDrawToken());
    171         uploadTarget.issueDrawToken();
    172         uploadTarget.flushToken();
    173         atlas->compact(uploadTarget.tokenTracker()->nextTokenToFlush());
    174     }
    175 
    176     check(reporter, atlas.get(), 1, 4, 1);
    177 }
    178 
    179 // This test verifies that the GrAtlasTextOp::onPrepare method correctly handles a failure
    180 // when allocating an atlas page.
    181 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrAtlasTextOpPreparation, reporter, ctxInfo) {
    182 
    183     auto context = ctxInfo.grContext();
    184 
    185     auto gpu = context->priv().getGpu();
    186     auto resourceProvider = context->priv().resourceProvider();
    187     auto drawingManager = context->priv().drawingManager();
    188     auto textContext = drawingManager->getTextContext();
    189     auto opMemoryPool = context->priv().opMemoryPool();
    190 
    191     GrBackendFormat format =
    192             context->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
    193 
    194     auto rtc =  context->priv().makeDeferredRenderTargetContext(format,
    195                                                                 SkBackingFit::kApprox,
    196                                                                 32, 32,
    197                                                                 kRGBA_8888_GrPixelConfig,
    198                                                                 nullptr);
    199 
    200     SkPaint paint;
    201     paint.setColor(SK_ColorRED);
    202 
    203     SkFont font;
    204     font.setEdging(SkFont::Edging::kAlias);
    205 
    206     const char* text = "a";
    207 
    208     std::unique_ptr<GrDrawOp> op = textContext->createOp_TestingOnly(
    209             context, textContext, rtc.get(), paint, font, SkMatrix::I(), text, 16, 16);
    210     op->finalize(*context->priv().caps(), nullptr, GrFSAAType::kNone, GrClampType::kAuto);
    211 
    212     TestingUploadTarget uploadTarget;
    213 
    214     GrOpFlushState flushState(gpu, resourceProvider, uploadTarget.writeableTokenTracker());
    215     GrOpFlushState::OpArgs opArgs = {
    216         op.get(),
    217         rtc->asRenderTargetProxy(),
    218         nullptr,
    219         GrXferProcessor::DstProxy(nullptr, SkIPoint::Make(0, 0))
    220     };
    221 
    222     // Cripple the atlas manager so it can't allocate any pages. This will force a failure
    223     // in the preparation of the text op
    224     auto atlasManager = context->priv().getAtlasManager();
    225     unsigned int numProxies;
    226     atlasManager->getProxies(kA8_GrMaskFormat, &numProxies);
    227     atlasManager->setMaxPages_TestingOnly(0);
    228 
    229     flushState.setOpArgs(&opArgs);
    230     op->prepare(&flushState);
    231     flushState.setOpArgs(nullptr);
    232     opMemoryPool->release(std::move(op));
    233 }
    234 
    235 void test_atlas_config(skiatest::Reporter* reporter, int maxTextureSize, size_t maxBytes,
    236                        GrMaskFormat maskFormat, SkISize expectedDimensions,
    237                        SkISize expectedPlotDimensions) {
    238     GrDrawOpAtlasConfig config(maxTextureSize, maxBytes);
    239     REPORTER_ASSERT(reporter, config.atlasDimensions(maskFormat) == expectedDimensions);
    240     REPORTER_ASSERT(reporter, config.plotDimensions(maskFormat) == expectedPlotDimensions);
    241 }
    242 
    243 DEF_GPUTEST(GrDrawOpAtlasConfig_Basic, reporter, options) {
    244     // 1/4 MB
    245     test_atlas_config(reporter, 65536, 256 * 1024, kARGB_GrMaskFormat,
    246                       { 256, 256 }, { 256, 256 });
    247     test_atlas_config(reporter, 65536, 256 * 1024, kA8_GrMaskFormat,
    248                       { 512, 512 }, { 256, 256 });
    249     // 1/2 MB
    250     test_atlas_config(reporter, 65536, 512 * 1024, kARGB_GrMaskFormat,
    251                       { 512, 256 }, { 256, 256 });
    252     test_atlas_config(reporter, 65536, 512 * 1024, kA8_GrMaskFormat,
    253                       { 1024, 512 }, { 256, 256 });
    254     // 1 MB
    255     test_atlas_config(reporter, 65536, 1024 * 1024, kARGB_GrMaskFormat,
    256                       { 512, 512 }, { 256, 256 });
    257     test_atlas_config(reporter, 65536, 1024 * 1024, kA8_GrMaskFormat,
    258                       { 1024, 1024 }, { 256, 256 });
    259     // 2 MB
    260     test_atlas_config(reporter, 65536, 2 * 1024 * 1024, kARGB_GrMaskFormat,
    261                       { 1024, 512 }, { 256, 256 });
    262     test_atlas_config(reporter, 65536, 2 * 1024 * 1024, kA8_GrMaskFormat,
    263                       { 2048, 1024 }, { 512, 256 });
    264     // 4 MB
    265     test_atlas_config(reporter, 65536, 4 * 1024 * 1024, kARGB_GrMaskFormat,
    266                       { 1024, 1024 }, { 256, 256 });
    267     test_atlas_config(reporter, 65536, 4 * 1024 * 1024, kA8_GrMaskFormat,
    268                       { 2048, 2048 }, { 512, 512 });
    269     // 8 MB
    270     test_atlas_config(reporter, 65536, 8 * 1024 * 1024, kARGB_GrMaskFormat,
    271                       { 2048, 1024 }, { 256, 256 });
    272     test_atlas_config(reporter, 65536, 8 * 1024 * 1024, kA8_GrMaskFormat,
    273                       { 2048, 2048 }, { 512, 512 });
    274     // 16 MB (should be same as 8 MB)
    275     test_atlas_config(reporter, 65536, 16 * 1024 * 1024, kARGB_GrMaskFormat,
    276                       { 2048, 1024 }, { 256, 256 });
    277     test_atlas_config(reporter, 65536, 16 * 1024 * 1024, kA8_GrMaskFormat,
    278                       { 2048, 2048 }, { 512, 512 });
    279 
    280     // 4MB, restricted texture size
    281     test_atlas_config(reporter, 1024, 8 * 1024 * 1024, kARGB_GrMaskFormat,
    282                       { 1024, 1024 }, { 256, 256 });
    283     test_atlas_config(reporter, 1024, 8 * 1024 * 1024, kA8_GrMaskFormat,
    284                       { 1024, 1024 }, { 256, 256 });
    285 
    286     // 3 MB (should be same as 2 MB)
    287     test_atlas_config(reporter, 65536, 3 * 1024 * 1024, kARGB_GrMaskFormat,
    288                       { 1024, 512 }, { 256, 256 });
    289     test_atlas_config(reporter, 65536, 3 * 1024 * 1024, kA8_GrMaskFormat,
    290                       { 2048, 1024 }, { 512, 256 });
    291 
    292     // minimum size
    293     test_atlas_config(reporter, 65536, 0, kARGB_GrMaskFormat,
    294                       { 256, 256 }, { 256, 256 });
    295     test_atlas_config(reporter, 65536, 0, kA8_GrMaskFormat,
    296                       { 512, 512 }, { 256, 256 });
    297 }
    298