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