/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef skiagm_DEFINED #define skiagm_DEFINED #include "gm/verifiers/gmverifier.h" #include "include/core/SkColor.h" #include "include/core/SkScalar.h" #include "include/core/SkSize.h" #include "include/core/SkString.h" #include "include/core/SkTypes.h" #include "include/private/SkMacros.h" #include "tools/Registry.h" #include class GrDirectContext; class GrRecordingContext; class GrSurfaceDrawContext; class SkCanvas; class SkMetaData; struct GrContextOptions; #define DEF_GM(CODE) \ static skiagm::GMRegistry SK_MACRO_APPEND_COUNTER(REG_)( \ []() { return std::unique_ptr([]() { CODE; }()); }); // A Simple GM is a rendering test that does not store state between rendering calls or make use of // the onOnceBeforeDraw() virtual; it consists of: // * A name. // * Prefered width and height. // * Optionally, a background color (default is white). // * A standalone function pointer that implements its onDraw method. #define DEF_SIMPLE_GM(NAME, CANVAS, W, H) \ DEF_SIMPLE_GM_BG_NAME(NAME, CANVAS, W, H, SK_ColorWHITE, SkString(#NAME)) #define DEF_SIMPLE_GM_BG(NAME, CANVAS, W, H, BGCOLOR) \ DEF_SIMPLE_GM_BG_NAME(NAME, CANVAS, W, H, BGCOLOR, SkString(#NAME)) #define DEF_SIMPLE_GM_BG_NAME(NAME, CANVAS, W, H, BGCOLOR, NAME_STR) \ static void SK_MACRO_CONCAT(NAME,_GM_inner)(SkCanvas*); \ DEF_SIMPLE_GM_BG_NAME_CAN_FAIL(NAME, CANVAS,, W, H, BGCOLOR, NAME_STR) { \ SK_MACRO_CONCAT(NAME,_GM_inner)(CANVAS); \ return skiagm::DrawResult::kOk; \ } \ void SK_MACRO_CONCAT(NAME,_GM_inner)(SkCanvas* CANVAS) #define DEF_SIMPLE_GM_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H) \ DEF_SIMPLE_GM_BG_NAME_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H, SK_ColorWHITE, SkString(#NAME)) #define DEF_SIMPLE_GM_BG_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H, BGCOLOR) \ DEF_SIMPLE_GM_BG_NAME_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H, BGCOLOR, SkString(#NAME)) #define DEF_SIMPLE_GM_BG_NAME_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H, BGCOLOR, NAME_STR) \ static skiagm::DrawResult SK_MACRO_CONCAT(NAME,_GM)(SkCanvas*, SkString*); \ DEF_GM(return new skiagm::SimpleGM(BGCOLOR, NAME_STR, {W,H}, SK_MACRO_CONCAT(NAME,_GM));) \ skiagm::DrawResult SK_MACRO_CONCAT(NAME,_GM)(SkCanvas* CANVAS, SkString* ERR_MSG) // A Simple GpuGM makes direct GPU calls. Its onDraw hook that includes GPU objects as params, and // is only invoked on GPU configs. Non-GPU configs automatically draw a GPU-only message and abort. #define DEF_SIMPLE_GPU_GM(NAME, GR_CONTEXT, SURFACE_DRAW_CONTEXT, CANVAS, W, H) \ DEF_SIMPLE_GPU_GM_BG(NAME, GR_CONTEXT, SURFACE_DRAW_CONTEXT, CANVAS, W, H, SK_ColorWHITE) #define DEF_SIMPLE_GPU_GM_BG(NAME, GR_CONTEXT, SURFACE_DRAW_CONTEXT, CANVAS, W, H, BGCOLOR) \ static void SK_MACRO_CONCAT(NAME,_GM_inner)(GrRecordingContext*, GrSurfaceDrawContext*, \ SkCanvas*); \ DEF_SIMPLE_GPU_GM_BG_CAN_FAIL(NAME, GR_CONTEXT, SURFACE_DRAW_CONTEXT, CANVAS,, W, H, \ BGCOLOR) { \ SK_MACRO_CONCAT(NAME,_GM_inner)(GR_CONTEXT, SURFACE_DRAW_CONTEXT, CANVAS); \ return skiagm::DrawResult::kOk; \ } \ void SK_MACRO_CONCAT(NAME,_GM_inner)( \ GrRecordingContext* GR_CONTEXT, GrSurfaceDrawContext* SURFACE_DRAW_CONTEXT, \ SkCanvas* CANVAS) #define DEF_SIMPLE_GPU_GM_CAN_FAIL(NAME, GR_CONTEXT, SURFACE_DRAW_CONTEXT, CANVAS, ERR_MSG, W, H) \ DEF_SIMPLE_GPU_GM_BG_CAN_FAIL(NAME, GR_CONTEXT, SURFACE_DRAW_CONTEXT, CANVAS, \ ERR_MSG, W, H, SK_ColorWHITE) #define DEF_SIMPLE_GPU_GM_BG_CAN_FAIL(NAME, GR_CONTEXT, SURFACE_DRAW_CONTEXT, CANVAS, ERR_MSG, W, \ H, BGCOLOR) \ static skiagm::DrawResult SK_MACRO_CONCAT(NAME,_GM)( \ GrRecordingContext*, GrSurfaceDrawContext*, SkCanvas*, SkString*); \ DEF_GM(return new skiagm::SimpleGpuGM(BGCOLOR, SkString(#NAME), {W,H}, \ SK_MACRO_CONCAT(NAME,_GM));) \ skiagm::DrawResult SK_MACRO_CONCAT(NAME,_GM)( \ GrRecordingContext* GR_CONTEXT, GrSurfaceDrawContext* SURFACE_DRAW_CONTEXT, \ SkCanvas* CANVAS, SkString* ERR_MSG) namespace skiagm { enum class DrawResult { kOk, // Test drew successfully. kFail, // Test failed to draw. kSkip // Test is not applicable in this context and should be skipped. }; class GM { public: using DrawResult = skiagm::DrawResult; GM(SkColor backgroundColor = SK_ColorWHITE); virtual ~GM(); enum Mode { kGM_Mode, kSample_Mode, kBench_Mode, }; void setMode(Mode mode) { fMode = mode; } Mode getMode() const { return fMode; } static constexpr char kErrorMsg_DrawSkippedGpuOnly[] = "This test is for GPU configs only."; DrawResult gpuSetup(GrDirectContext* context, SkCanvas* canvas) { SkString errorMsg; return this->gpuSetup(context, canvas, &errorMsg); } DrawResult gpuSetup(GrDirectContext*, SkCanvas*, SkString* errorMsg); void gpuTeardown(); void onceBeforeDraw() { if (!fHaveCalledOnceBeforeDraw) { fHaveCalledOnceBeforeDraw = true; this->onOnceBeforeDraw(); } } DrawResult draw(SkCanvas* canvas) { SkString errorMsg; return this->draw(canvas, &errorMsg); } DrawResult draw(SkCanvas*, SkString* errorMsg); void drawBackground(SkCanvas*); DrawResult drawContent(SkCanvas* canvas) { SkString errorMsg; return this->drawContent(canvas, &errorMsg); } DrawResult drawContent(SkCanvas*, SkString* errorMsg); SkISize getISize() { return this->onISize(); } const char* getName(); virtual bool runAsBench() const; SkScalar width() { return SkIntToScalar(this->getISize().width()); } SkScalar height() { return SkIntToScalar(this->getISize().height()); } SkColor getBGColor() const { return fBGColor; } void setBGColor(SkColor); // helper: fill a rect in the specified color based on the GM's getISize bounds. void drawSizeBounds(SkCanvas*, SkColor); bool animate(double /*nanos*/); virtual bool onChar(SkUnichar); bool getControls(SkMetaData* controls) { return this->onGetControls(controls); } void setControls(const SkMetaData& controls) { this->onSetControls(controls); } virtual void modifyGrContextOptions(GrContextOptions*); virtual std::unique_ptr getVerifiers() const; protected: // onGpuSetup is called once before any other processing with a direct context. virtual DrawResult onGpuSetup(GrDirectContext*, SkString*) { return DrawResult::kOk; } virtual void onGpuTeardown() {} virtual void onOnceBeforeDraw(); virtual DrawResult onDraw(SkCanvas*, SkString* errorMsg); virtual void onDraw(SkCanvas*); virtual SkISize onISize() = 0; virtual SkString onShortName() = 0; virtual bool onAnimate(double /*nanos*/); virtual bool onGetControls(SkMetaData*); virtual void onSetControls(const SkMetaData&); private: Mode fMode; SkString fShortName; SkColor fBGColor; bool fHaveCalledOnceBeforeDraw = false; bool fGpuSetup = false; DrawResult fGpuSetupResult = DrawResult::kOk; }; using GMFactory = std::unique_ptr (*)(); using GMRegistry = sk_tools::Registry; // A GpuGM replaces the onDraw method with one that also accepts GPU objects alongside the // SkCanvas. Its onDraw is only invoked on GPU configs; on non-GPU configs it will automatically // draw a GPU-only message and abort. class GpuGM : public GM { public: GpuGM(SkColor backgroundColor = SK_ColorWHITE) : GM(backgroundColor) {} // TODO(tdenniston): Currently GpuGMs don't have verifiers (because they do not render on // CPU), but we may want to be able to verify the output images standalone, without // requiring a gold image for comparison. std::unique_ptr getVerifiers() const override { return nullptr; } private: using GM::onDraw; DrawResult onDraw(SkCanvas*, SkString* errorMsg) final; virtual DrawResult onDraw(GrRecordingContext*, GrSurfaceDrawContext*, SkCanvas*, SkString* errorMsg); virtual void onDraw(GrRecordingContext*, GrSurfaceDrawContext*, SkCanvas*); }; // SimpleGM is intended for basic GMs that can define their entire implementation inside a // single "draw" function pointer. class SimpleGM : public GM { public: using DrawProc = DrawResult(*)(SkCanvas*, SkString*); SimpleGM(SkColor bgColor, const SkString& name, const SkISize& size, DrawProc drawProc) : GM(bgColor), fName(name), fSize(size), fDrawProc(drawProc) {} private: SkISize onISize() override; SkString onShortName() override; DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override; const SkString fName; const SkISize fSize; const DrawProc fDrawProc; }; class SimpleGpuGM : public GpuGM { public: using DrawProc = DrawResult (*)(GrRecordingContext*, GrSurfaceDrawContext*, SkCanvas*, SkString* errorMsg); SimpleGpuGM(SkColor bgColor, const SkString& name, const SkISize& size, DrawProc drawProc) : GpuGM(bgColor), fName(name), fSize(size), fDrawProc(drawProc) {} private: SkISize onISize() override; SkString onShortName() override; DrawResult onDraw(GrRecordingContext*, GrSurfaceDrawContext*, SkCanvas*, SkString* errorMsg) override; const SkString fName; const SkISize fSize; const DrawProc fDrawProc; }; } // namespace skiagm void MarkGMGood(SkCanvas*, SkScalar x, SkScalar y); void MarkGMBad (SkCanvas*, SkScalar x, SkScalar y); #endif