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