Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2017 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 "Test.h"
      9 
     10 #include "SkPath.h"
     11 
     12 #if SK_SUPPORT_GPU
     13 #include "GrClip.h"
     14 #include "GrContext.h"
     15 #include "GrContextPriv.h"
     16 #include "GrResourceCache.h"
     17 #include "GrSoftwarePathRenderer.h"
     18 #include "effects/GrPorterDuffXferProcessor.h"
     19 #include "ops/GrTessellatingPathRenderer.h"
     20 
     21 static SkPath create_concave_path() {
     22     SkPath path;
     23     path.moveTo(100, 0);
     24     path.lineTo(200, 200);
     25     path.lineTo(100, 150);
     26     path.lineTo(0, 200);
     27     path.close();
     28     return path;
     29 }
     30 
     31 static void draw_path(GrContext* ctx,
     32                       GrRenderTargetContext* renderTargetContext,
     33                       const SkPath& path,
     34                       GrPathRenderer* pr,
     35                       GrAAType aaType,
     36                       const GrStyle& style) {
     37     GrPaint paint;
     38     paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
     39 
     40     GrNoClip noClip;
     41     SkIRect clipConservativeBounds = SkIRect::MakeWH(renderTargetContext->width(),
     42                                                      renderTargetContext->height());
     43     GrShape shape(path, style);
     44     if (shape.style().applies()) {
     45         shape = shape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, 1.0f);
     46     }
     47     SkMatrix matrix = SkMatrix::I();
     48     GrPathRenderer::DrawPathArgs args{ctx,
     49                                       std::move(paint),
     50                                       &GrUserStencilSettings::kUnused,
     51                                       renderTargetContext,
     52                                       &noClip,
     53                                       &clipConservativeBounds,
     54                                       &matrix,
     55                                       &shape,
     56                                       aaType,
     57                                       false};
     58     pr->drawPath(args);
     59 }
     60 
     61 static bool cache_non_scratch_resources_equals(GrResourceCache* cache, int expected) {
     62 #if GR_CACHE_STATS
     63     GrResourceCache::Stats stats;
     64     cache->getStats(&stats);
     65     return (stats.fTotal - stats.fScratch) == expected;
     66 #else
     67     return true;
     68 #endif
     69 }
     70 
     71 static void test_path(skiatest::Reporter* reporter,
     72                       std::function<SkPath(void)> createPath,
     73                       std::function<GrPathRenderer*(GrContext*)> createPathRenderer,
     74                       int expected,
     75                       GrAAType aaType = GrAAType::kNone,
     76                       GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) {
     77     sk_sp<GrContext> ctx = GrContext::MakeMock(nullptr);
     78     // The cache needs to be big enough that nothing gets flushed, or our expectations can be wrong
     79     ctx->setResourceCacheLimits(100, 8000000);
     80     GrResourceCache* cache = ctx->contextPriv().getResourceCache();
     81 
     82     sk_sp<GrRenderTargetContext> rtc(ctx->makeDeferredRenderTargetContext(
     83             SkBackingFit::kApprox, 800, 800, kRGBA_8888_GrPixelConfig, nullptr, 1, GrMipMapped::kNo,
     84             kTopLeft_GrSurfaceOrigin));
     85     if (!rtc) {
     86         return;
     87     }
     88 
     89     sk_sp<GrPathRenderer> pathRenderer(createPathRenderer(ctx.get()));
     90     SkPath path = createPath();
     91 
     92     // Initially, cache only has the render target context
     93     REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, 0));
     94 
     95     // Draw the path, check that new resource count matches expectations
     96     draw_path(ctx.get(), rtc.get(), path, pathRenderer.get(), aaType, style);
     97     ctx->flush();
     98     REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected));
     99 
    100     // Nothing should be purgeable yet
    101     cache->purgeAsNeeded();
    102     REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected));
    103 
    104     // Reset the path to change the GenID, which should invalidate one resource in the cache.
    105     // Some path renderers may leave other unique-keyed resources in the cache, though.
    106     path.reset();
    107     cache->purgeAsNeeded();
    108     REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected - 1));
    109 }
    110 
    111 // Test that deleting the original path invalidates the VBs cached by the tessellating path renderer
    112 DEF_GPUTEST(TessellatingPathRendererCacheTest, reporter, /* options */) {
    113     auto createPR = [](GrContext*) {
    114         return new GrTessellatingPathRenderer();
    115     };
    116 
    117     // Tessellating path renderer creates a single vertex buffer for non-AA paths. No other
    118     // resources should be created.
    119     const int kExpectedResources = 1;
    120 
    121     test_path(reporter, create_concave_path, createPR, kExpectedResources);
    122 
    123     // Test with a style that alters the path geometry. This needs to attach the invalidation logic
    124     // to the original path, not the modified path produced by the style.
    125     SkPaint paint;
    126     paint.setStyle(SkPaint::kStroke_Style);
    127     paint.setStrokeWidth(1);
    128     GrStyle style(paint);
    129     test_path(reporter, create_concave_path, createPR, kExpectedResources, GrAAType::kNone, style);
    130 }
    131 
    132 // Test that deleting the original path invalidates the textures cached by the SW path renderer
    133 DEF_GPUTEST(SoftwarePathRendererCacheTest, reporter, /* options */) {
    134     auto createPR = [](GrContext* ctx) {
    135         return new GrSoftwarePathRenderer(ctx->contextPriv().proxyProvider(), true);
    136     };
    137 
    138     // Software path renderer creates a mask texture, but also renders with a non-AA rect, which
    139     // refs the quad index buffer.
    140     const int kExpectedResources = 2;
    141 
    142     test_path(reporter, create_concave_path, createPR, kExpectedResources, GrAAType::kCoverage);
    143 
    144     // Test with a style that alters the path geometry. This needs to attach the invalidation logic
    145     // to the original path, not the modified path produced by the style.
    146     SkPaint paint;
    147     paint.setStyle(SkPaint::kStroke_Style);
    148     paint.setStrokeWidth(1);
    149     GrStyle style(paint);
    150     test_path(reporter, create_concave_path, createPR, kExpectedResources, GrAAType::kCoverage,
    151               style);
    152 }
    153 
    154 #endif
    155