xref: /aosp_15_r20/external/skia/gm/gm.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 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 #ifndef skiagm_DEFINED
9 #define skiagm_DEFINED
10 
11 #include "include/core/SkColor.h"
12 #include "include/core/SkScalar.h"
13 #include "include/core/SkSize.h"
14 #include "include/core/SkString.h"
15 #include "include/core/SkTypes.h"
16 #include "include/private/base/SkMacros.h"
17 #include "tools/Registry.h"
18 
19 #include <functional>
20 #include <map>
21 #include <memory>
22 
23 class GrRecordingContext;
24 class SkCanvas;
25 class SkMetaData;
26 struct GrContextOptions;
27 
28 namespace skiagm::verifiers {
29 class VerifierList;
30 }
31 
32 namespace skgpu::graphite {
33 struct ContextOptions;
34 }
35 
36 namespace skiatest::graphite {
37 class GraphiteTestContext;
38 }
39 
40 #define DEF_GM(CODE)                                         \
41     static skiagm::GMRegistry SK_MACRO_APPEND_COUNTER(REG_)( \
42             []() { return std::unique_ptr<skiagm::GM>([]() { CODE; }()); });
43 
44 // A Simple GM is a rendering test that does not store state between rendering calls or make use of
45 // the onOnceBeforeDraw() virtual; it consists of:
46 //   *   A name.
47 //   *   Prefered width and height.
48 //   *   Optionally, a background color (default is white).
49 //   *   A standalone function pointer that implements its onDraw method.
50 #define DEF_SIMPLE_GM(NAME, CANVAS, W, H) \
51     DEF_SIMPLE_GM_BG_NAME(NAME, CANVAS, W, H, SK_ColorWHITE, SkString(#NAME))
52 #define DEF_SIMPLE_GM_BG(NAME, CANVAS, W, H, BGCOLOR) \
53     DEF_SIMPLE_GM_BG_NAME(NAME, CANVAS, W, H, BGCOLOR, SkString(#NAME))
54 #define DEF_SIMPLE_GM_BG_NAME(NAME, CANVAS, W, H, BGCOLOR, NAME_STR) \
55     static void SK_MACRO_CONCAT(NAME,_GM_inner)(SkCanvas*); \
56     DEF_SIMPLE_GM_BG_NAME_CAN_FAIL(NAME, CANVAS,, W, H, BGCOLOR, NAME_STR) { \
57         SK_MACRO_CONCAT(NAME,_GM_inner)(CANVAS); \
58         return skiagm::DrawResult::kOk; \
59     } \
60     void SK_MACRO_CONCAT(NAME,_GM_inner)(SkCanvas* CANVAS)
61 
62 #define DEF_SIMPLE_GM_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H) \
63     DEF_SIMPLE_GM_BG_NAME_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H, SK_ColorWHITE, SkString(#NAME))
64 #define DEF_SIMPLE_GM_BG_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H, BGCOLOR) \
65     DEF_SIMPLE_GM_BG_NAME_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H, BGCOLOR, SkString(#NAME))
66 #define DEF_SIMPLE_GM_BG_NAME_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H, BGCOLOR, NAME_STR) \
67     static skiagm::DrawResult SK_MACRO_CONCAT(NAME,_GM)(SkCanvas*, SkString*); \
68     DEF_GM(return new skiagm::SimpleGM(BGCOLOR, NAME_STR, {W,H}, SK_MACRO_CONCAT(NAME,_GM));) \
69     skiagm::DrawResult SK_MACRO_CONCAT(NAME,_GM)(SkCanvas* CANVAS, SkString* ERR_MSG)
70 
71 // Declares a function that dynamically registers GMs (e.g. based on some command-line flag). See
72 // the GMRegistererFnRegistry definition below for additional context.
73 #define DEF_GM_REGISTERER_FN(FN) \
74     static skiagm::GMRegistererFnRegistry SK_MACRO_APPEND_COUNTER(REG_)(FN)
75 
76 #if defined(SK_GANESH)
77 // A Simple GpuGM makes direct GPU calls. Its onDraw hook that includes GPU objects as params, and
78 // is only invoked on GPU configs. Non-GPU configs automatically draw a GPU-only message and abort.
79 #define DEF_SIMPLE_GPU_GM(NAME, GR_CONTEXT, CANVAS, W, H)                                         \
80     DEF_SIMPLE_GPU_GM_BG(NAME, GR_CONTEXT, CANVAS, W, H, SK_ColorWHITE)
81 
82 #define DEF_SIMPLE_GPU_GM_BG(NAME, GR_CONTEXT, CANVAS, W, H, BGCOLOR)                             \
83     static void SK_MACRO_CONCAT(NAME,_GM_inner)(GrRecordingContext*, SkCanvas*);                  \
84     DEF_SIMPLE_GPU_GM_BG_CAN_FAIL(NAME, GR_CONTEXT, CANVAS, /* ERR_MSG */, W, H, BGCOLOR) {       \
85         SK_MACRO_CONCAT(NAME,_GM_inner)(GR_CONTEXT, CANVAS);                                      \
86         return skiagm::DrawResult::kOk;                                                           \
87     }                                                                                             \
88     void SK_MACRO_CONCAT(NAME,_GM_inner)(GrRecordingContext* GR_CONTEXT, SkCanvas* CANVAS)
89 
90 #define DEF_SIMPLE_GPU_GM_CAN_FAIL(NAME, GR_CONTEXT, CANVAS, ERR_MSG, W, H)                       \
91     DEF_SIMPLE_GPU_GM_BG_CAN_FAIL(NAME, GR_CONTEXT, CANVAS, ERR_MSG, W, H, SK_ColorWHITE)
92 
93 #define DEF_SIMPLE_GPU_GM_BG_CAN_FAIL(NAME, GR_CONTEXT, CANVAS, ERR_MSG, W, H, BGCOLOR)           \
94     static skiagm::DrawResult SK_MACRO_CONCAT(NAME,_GM)(                                          \
95         GrRecordingContext*, SkCanvas*, SkString*);                                               \
96     DEF_GM(return new skiagm::SimpleGpuGM(BGCOLOR, SkString(#NAME), {W,H},                        \
97                                           SK_MACRO_CONCAT(NAME,_GM));)                            \
98     skiagm::DrawResult SK_MACRO_CONCAT(NAME,_GM)(                                                 \
99             GrRecordingContext* GR_CONTEXT, SkCanvas* CANVAS, SkString* ERR_MSG)
100 #endif
101 
102 namespace skiagm {
103 
104     enum class DrawResult {
105         kOk,   // Test drew successfully.
106         kFail, // Test failed to draw.
107         kSkip  // Test is not applicable in this context and should be skipped.
108     };
109 
110     class GM {
111     public:
112         using DrawResult = skiagm::DrawResult;
113         using GraphiteTestContext = skiatest::graphite::GraphiteTestContext;
114 
115         GM(SkColor backgroundColor = SK_ColorWHITE);
116         virtual ~GM();
117 
118         enum Mode {
119             kGM_Mode,
120             kSample_Mode,
121             kBench_Mode,
122         };
123 
setMode(Mode mode)124         void setMode(Mode mode) { fMode = mode; }
getMode()125         Mode getMode() const { return fMode; }
126 
127         inline static constexpr char kErrorMsg_DrawSkippedGpuOnly[] =
128                 "This test is for GPU configs only.";
129 
130         DrawResult gpuSetup(SkCanvas*, SkString* errorMsg, GraphiteTestContext* = nullptr);
131         void gpuTeardown();
132 
onceBeforeDraw()133         void onceBeforeDraw() {
134             if (!fHaveCalledOnceBeforeDraw) {
135                 fHaveCalledOnceBeforeDraw = true;
136                 this->onOnceBeforeDraw();
137             }
138         }
139 
draw(SkCanvas * canvas)140         DrawResult draw(SkCanvas* canvas) {
141             SkString errorMsg;
142             return this->draw(canvas, &errorMsg);
143         }
144         DrawResult draw(SkCanvas*, SkString* errorMsg);
145 
146         void drawBackground(SkCanvas*);
drawContent(SkCanvas * canvas)147         DrawResult drawContent(SkCanvas* canvas) {
148             SkString errorMsg;
149             return this->drawContent(canvas, &errorMsg);
150         }
151         DrawResult drawContent(SkCanvas*, SkString* errorMsg);
152 
153         virtual SkISize getISize() = 0;
154 
155         virtual SkString getName() const = 0;
156 
157         virtual bool runAsBench() const;
158 
width()159         SkScalar width() {
160             return SkIntToScalar(this->getISize().width());
161         }
height()162         SkScalar height() {
163             return SkIntToScalar(this->getISize().height());
164         }
165 
getBGColor()166         SkColor getBGColor() const { return fBGColor; }
167         void setBGColor(SkColor);
168 
169         // helper: fill a rect in the specified color based on the GM's getISize bounds.
170         void drawSizeBounds(SkCanvas*, SkColor);
171 
172         bool animate(double /*nanos*/);
173         virtual bool onChar(SkUnichar);
174 
getControls(SkMetaData * controls)175         bool getControls(SkMetaData* controls) { return this->onGetControls(controls); }
setControls(const SkMetaData & controls)176         void setControls(const SkMetaData& controls) { this->onSetControls(controls); }
177 
modifyGrContextOptions(GrContextOptions *)178         virtual void modifyGrContextOptions(GrContextOptions*) {}
modifyGraphiteContextOptions(skgpu::graphite::ContextOptions *)179         virtual void modifyGraphiteContextOptions(skgpu::graphite::ContextOptions*) const {}
180 
181         // Convenience method to skip Bazel-only GMs from DM.
182         //
183         // As of Q3 2023, lovisolo@ is experimenting with reimplementing some DM behaviors as
184         // smaller, independent Bazel targets. For example, file
185         // //tools/testrunners/gm/BazelGMTestRunner.cpp provides a main function that can run GMs.
186         // With this file, one can define multiple small Bazel tests to run groups of related GMs
187         // with Bazel. However, GMs are only one kind of "source" supported by DM (see class
188         // GMSrc). DM supports other kinds of sources as well, such as codecs (CodecSrc class) and
189         // image generators (ImageGenSrc class). One possible strategy to support these sources in
190         // our Bazel build is to turn them into GMs. For example, instead of using the CodecSrc
191         // class from Bazel, we could have a GM subclass that takes an image as an input, decodes
192         // it using a codec, and draws in on a canvas. Given that this overlaps with existing DM
193         // functionality, we would mark such GMs as Bazel-only.
194         //
195         // Another possibility is to slowly replace all existing DM source types with just GMs.
196         // This would lead to a simpler DM architecture where there is only one source type and
197         // multiple sinks, as opposed to the current design with multiple sources and sinks.
198         // Furthermore, it would simplify the migration to Bazel because it would allow us to
199         // leverage existing work to run GMs with Bazel.
200         //
201         // TODO(lovisolo): Delete once it's no longer needed.
isBazelOnly()202         virtual bool isBazelOnly() const { return false; }
203 
204         // Ignored by DM. Returns the set of Gold key/value pairs specific to this GM, such as the
205         // GM name and corpus. GMs may define additional keys. For example, codec GMs define keys
206         // for the parameters utilized to initialize the codec.
getGoldKeys()207         virtual std::map<std::string, std::string> getGoldKeys() const {
208             return std::map<std::string, std::string>{
209                     {"name", getName().c_str()},
210                     {"source_type", "gm"},
211             };
212         }
213 
214     protected:
215         // onGpuSetup is called once before any other processing with a direct context.
onGpuSetup(SkCanvas *,SkString *,GraphiteTestContext *)216         virtual DrawResult onGpuSetup(SkCanvas*, SkString*, GraphiteTestContext*) {
217             return DrawResult::kOk;
218         }
onGpuTeardown()219         virtual void onGpuTeardown() {}
220         virtual void onOnceBeforeDraw();
221         virtual DrawResult onDraw(SkCanvas*, SkString* errorMsg);
222         virtual void onDraw(SkCanvas*);
223 
224         virtual bool onAnimate(double /*nanos*/);
225         virtual bool onGetControls(SkMetaData*);
226         virtual void onSetControls(const SkMetaData&);
227 
graphiteTestContext()228         GraphiteTestContext* graphiteTestContext() const { return fGraphiteTestContext; }
229 
230     private:
231         Mode fMode;
232         SkColor    fBGColor;
233         bool       fHaveCalledOnceBeforeDraw = false;
234         bool       fGpuSetup = false;
235         DrawResult fGpuSetupResult = DrawResult::kOk;
236         GraphiteTestContext* fGraphiteTestContext;
237     };
238 
239     using GMFactory = std::function<std::unique_ptr<skiagm::GM>()>;
240     using GMRegistry = sk_tools::Registry<GMFactory>;
241 
242     // Adds a GM to the GMRegistry.
243     void Register(skiagm::GM* gm);
244 
245     // Registry of functions that dynamically register GMs. Useful for GMs that are unknown at
246     // compile time, such as those that are created from images in a directory (see e.g.
247     // //gm/png_codec.cpp).
248     //
249     // A GMRegistererFn may call skiagm::Register() zero or more times to register GMs as needed.
250     // It should return the empty string on success, or a human-friendly message in the case of
251     // errors.
252     //
253     // Only used by //tools/testrunners/gm/BazelGMTestRunner.cpp for now.
254     using GMRegistererFn = std::function<std::string()>;
255     using GMRegistererFnRegistry = sk_tools::Registry<GMRegistererFn>;
256 
257 #if defined(SK_GANESH)
258     // A GpuGM replaces the onDraw method with one that also accepts GPU objects alongside the
259     // SkCanvas. Its onDraw is only invoked on GPU configs; on non-GPU configs it will automatically
260     // draw a GPU-only message and abort.
261     class GpuGM : public GM {
262     public:
GM(backgroundColor)263         GpuGM(SkColor backgroundColor = SK_ColorWHITE) : GM(backgroundColor) {}
264 
265     private:
266         using GM::onDraw;
267         DrawResult onDraw(SkCanvas*, SkString* errorMsg) final;
268 
269         virtual DrawResult onDraw(GrRecordingContext*, SkCanvas*, SkString* errorMsg);
270         virtual void onDraw(GrRecordingContext*, SkCanvas*);
271     };
272 #endif
273 
274     // SimpleGM is intended for basic GMs that can define their entire implementation inside a
275     // single "draw" function pointer.
276     class SimpleGM : public GM {
277     public:
278         using DrawProc = DrawResult(*)(SkCanvas*, SkString*);
279 
SimpleGM(SkColor bgColor,const SkString & name,const SkISize & size,DrawProc drawProc)280         SimpleGM(SkColor bgColor, const SkString& name, const SkISize& size, DrawProc drawProc)
281                 : GM(bgColor), fName(name), fSize(size), fDrawProc(drawProc) {}
282 
283         SkString getName() const override;
284         SkISize getISize() override;
285 
286     private:
287         DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override;
288 
289         const SkString fName;
290         const SkISize fSize;
291         const DrawProc fDrawProc;
292     };
293 
294 #if defined(SK_GANESH)
295     class SimpleGpuGM : public GpuGM {
296     public:
297         using DrawProc = DrawResult (*)(GrRecordingContext*, SkCanvas*, SkString* errorMsg);
298 
SimpleGpuGM(SkColor bgColor,const SkString & name,const SkISize & size,DrawProc drawProc)299         SimpleGpuGM(SkColor bgColor, const SkString& name, const SkISize& size, DrawProc drawProc)
300                 : GpuGM(bgColor), fName(name), fSize(size), fDrawProc(drawProc) {}
301 
302         SkString getName() const override;
303         SkISize getISize() override;
304 
305     private:
306         DrawResult onDraw(GrRecordingContext*, SkCanvas*, SkString* errorMsg) override;
307 
308         const SkString fName;
309         const SkISize fSize;
310         const DrawProc fDrawProc;
311     };
312 #endif
313 }  // namespace skiagm
314 
315 void MarkGMGood(SkCanvas*, SkScalar x, SkScalar y);
316 void MarkGMBad (SkCanvas*, SkScalar x, SkScalar y);
317 
318 #endif
319