• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 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 "sk_tool_utils.h"
9 
10 #include "SkCanvas.h"
11 #include "SkFontMgr.h"
12 #include "SkGlyphRun.h"
13 #include "SkGraphics.h"
14 #include "SkPaint.h"
15 #include "SkPoint.h"
16 #include "SkRandomScalerContext.h"
17 #include "SkSurface.h"
18 #include "SkTextBlob.h"
19 #include "SkTypeface.h"
20 
21 #ifdef SK_BUILD_FOR_WIN
22     #include "SkTypeface_win.h"
23 #endif
24 
25 #include "Test.h"
26 
27 #include "GrContext.h"
28 #include "GrContextPriv.h"
29 
draw(SkCanvas * canvas,int redraw,const SkTArray<sk_sp<SkTextBlob>> & blobs)30 static void draw(SkCanvas* canvas, int redraw, const SkTArray<sk_sp<SkTextBlob>>& blobs) {
31     int yOffset = 0;
32     for (int r = 0; r < redraw; r++) {
33         for (int i = 0; i < blobs.count(); i++) {
34             const auto& blob = blobs[i];
35             const SkRect& bounds = blob->bounds();
36             yOffset += SkScalarCeilToInt(bounds.height());
37             SkPaint paint;
38             canvas->drawTextBlob(blob, 0, SkIntToScalar(yOffset), paint);
39         }
40     }
41 }
42 
43 static const int kWidth = 1024;
44 static const int kHeight = 768;
45 
setup_always_evict_atlas(GrContext * context)46 static void setup_always_evict_atlas(GrContext* context) {
47     context->contextPriv().getAtlasManager()->setAtlasSizesToMinimum_ForTesting();
48 }
49 
50 // This test hammers the GPU textblobcache and font atlas
text_blob_cache_inner(skiatest::Reporter * reporter,GrContext * context,int maxTotalText,int maxGlyphID,int maxFamilies,bool normal,bool stressTest)51 static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContext* context,
52                                   int maxTotalText, int maxGlyphID, int maxFamilies, bool normal,
53                                   bool stressTest) {
54     // setup surface
55     uint32_t flags = 0;
56     SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
57 
58     // configure our context for maximum stressing of cache and atlas
59     if (stressTest) {
60         setup_always_evict_atlas(context);
61         context->contextPriv().setTextBlobCacheLimit_ForTesting(0);
62     }
63 
64     SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType);
65     auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, &props));
66     REPORTER_ASSERT(reporter, surface);
67     if (!surface) {
68         return;
69     }
70 
71     SkCanvas* canvas = surface->getCanvas();
72 
73     sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
74 
75     int count = SkMin32(fm->countFamilies(), maxFamilies);
76 
77     // make a ton of text
78     SkAutoTArray<uint16_t> text(maxTotalText);
79     for (int i = 0; i < maxTotalText; i++) {
80         text[i] = i % maxGlyphID;
81     }
82 
83     // generate textblobs
84     SkTArray<sk_sp<SkTextBlob>> blobs;
85     for (int i = 0; i < count; i++) {
86         SkFont font;
87         font.setSize(48); // draw big glyphs to really stress the atlas
88 
89         SkString familyName;
90         fm->getFamilyName(i, &familyName);
91         sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
92         for (int j = 0; j < set->count(); ++j) {
93             SkFontStyle fs;
94             set->getStyle(j, &fs, nullptr);
95 
96             // We use a typeface which randomy returns unexpected mask formats to fuzz
97             sk_sp<SkTypeface> orig(set->createTypeface(j));
98             if (normal) {
99                 font.setTypeface(orig);
100             } else {
101                 font.setTypeface(sk_make_sp<SkRandomTypeface>(orig, SkPaint(), true));
102             }
103 
104             SkTextBlobBuilder builder;
105             for (int aa = 0; aa < 2; aa++) {
106                 for (int subpixel = 0; subpixel < 2; subpixel++) {
107                     for (int lcd = 0; lcd < 2; lcd++) {
108                         font.setEdging(SkFont::Edging::kAlias);
109                         if (aa) {
110                             font.setEdging(SkFont::Edging::kAntiAlias);
111                             if (lcd) {
112                                 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
113                             }
114                         }
115                         font.setSubpixel(SkToBool(subpixel));
116                         if (!SkToBool(lcd)) {
117                             font.setSize(160);
118                         }
119                         const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(font,
120                                                                                    maxTotalText,
121                                                                                    0, 0,
122                                                                                    nullptr);
123                         memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t));
124                     }
125                 }
126             }
127             blobs.emplace_back(builder.make());
128         }
129     }
130 
131     // create surface where LCD is impossible
132     info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
133     SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry);
134     auto surfaceNoLCD(canvas->makeSurface(info, &propsNoLCD));
135     REPORTER_ASSERT(reporter, surface);
136     if (!surface) {
137         return;
138     }
139 
140     SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas();
141 
142     // test redraw
143     draw(canvas, 2, blobs);
144     draw(canvasNoLCD, 2, blobs);
145 
146     // test draw after free
147     context->freeGpuResources();
148     draw(canvas, 1, blobs);
149 
150     context->freeGpuResources();
151     draw(canvasNoLCD, 1, blobs);
152 
153     // test draw after abandon
154     context->abandonContext();
155     draw(canvas, 1, blobs);
156 }
157 
DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobCache,reporter,ctxInfo)158 DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobCache, reporter, ctxInfo) {
159     text_blob_cache_inner(reporter, ctxInfo.grContext(), 1024, 256, 30, true, false);
160 }
161 
DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressCache,reporter,ctxInfo)162 DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressCache, reporter, ctxInfo) {
163     text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, true, true);
164 }
165 
DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobAbnormal,reporter,ctxInfo)166 DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobAbnormal, reporter, ctxInfo) {
167     text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, false);
168 }
169 
DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressAbnormal,reporter,ctxInfo)170 DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressAbnormal, reporter, ctxInfo) {
171     text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, true);
172 }
173