/* * Copyright 2020 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "tests/Test.h" #include "src/gpu/GrFragmentProcessor.h" #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" #include "src/gpu/v1/SurfaceDrawContext_v1.h" static void run_test(skiatest::Reporter*, GrDirectContext*, skgpu::v1::SurfaceDrawContext*, SkVector a, SkVector b, float expectedCrossProduct); // This is a GPU test that ensures the SkSL 2d cross() intrinsic returns the correct sign (negative, // positive, or zero). DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkSLCross, reporter, ctxInfo) { GrDirectContext* dContext = ctxInfo.directContext(); auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact, {1, 1}, SkSurfaceProps()); if (!sdc) { ERRORF(reporter, "could not create render target context."); return; } run_test(reporter, dContext, sdc.get(), {3,4}, {5,6}, -2); // Negative. run_test(reporter, dContext, sdc.get(), {3,4}, {-5,-6}, 2); // Positive. run_test(reporter, dContext, sdc.get(), {0, 2.287f}, {0, -7.741f}, 0); // Zero. run_test(reporter, dContext, sdc.get(), {62.17f, 0}, {-43.49f, 0}, 0); // Zero. } namespace { // Outputs: // Green if cross(a,b) > 0 // Red if cross(a,b) < 0 // Black if cross(a,b) == 0 class VisualizeCrossProductSignFP : public GrFragmentProcessor { public: VisualizeCrossProductSignFP(SkVector a, SkVector b) : GrFragmentProcessor(kTestFP_ClassID, kPreservesOpaqueInput_OptimizationFlag) , fA(a), fB(b) { } const char* name() const override { return "VisualizeCrossProductSignFP"; } std::unique_ptr clone() const override { return std::unique_ptr(new VisualizeCrossProductSignFP(fA, fB)); } private: void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {} bool onIsEqual(const GrFragmentProcessor&) const override { return true; } std::unique_ptr onMakeProgramImpl() const override { class Impl : public ProgramImpl { public: void emitCode(EmitArgs& args) override { auto& fp = args.fFp.cast(); const char *a, *b; fAUniform = args.fUniformHandler->addUniform(&fp, kFragment_GrShaderFlag, SkSLType::kFloat2, "a", &a); fBUniform = args.fUniformHandler->addUniform(&fp, kFragment_GrShaderFlag, SkSLType::kFloat2, "b", &b); args.fFragBuilder->codeAppendf(R"( float crossProduct = cross_length_2d(%s, %s); float2 visualization = clamp(float2(-sign(crossProduct), sign(crossProduct)), float2(0), float2(1)); return half2(visualization).xy01;)", a, b); } private: void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) override { const auto& fp = processor.cast(); pdman.set2f(fAUniform, fp.fA.x(), fp.fA.y()); pdman.set2f(fBUniform, fp.fB.x(), fp.fB.y()); } GrGLSLUniformHandler::UniformHandle fAUniform; GrGLSLUniformHandler::UniformHandle fBUniform; }; return std::make_unique(); } const SkVector fA, fB; }; } // namespace static void run_test(skiatest::Reporter* reporter, GrDirectContext* directContext, skgpu::v1::SurfaceDrawContext* sdc, SkVector a, SkVector b, float expectedCrossProduct) { SkASSERT(sdc->width() == 1); SkASSERT(sdc->height() == 1); sdc->clear(SkPMColor4f::FromBytes_RGBA(0xbaaaaaad)); GrPaint crossPaint; crossPaint.setColor4f(SK_PMColor4fWHITE); crossPaint.setPorterDuffXPFactory(SkBlendMode::kSrcOver); crossPaint.setColorFragmentProcessor(std::make_unique(a, b)); sdc->drawRect(/*clip=*/nullptr, std::move(crossPaint), GrAA::kNo, SkMatrix::I(), SkRect::MakeWH(1,1)); GrColor result; GrPixmap resultPM(SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType), &result, sizeof(GrColor)); sdc->readPixels(directContext, resultPM, {0, 0}); SkASSERT(expectedCrossProduct == a.cross(b)); if (expectedCrossProduct > 0) { REPORTER_ASSERT(reporter, result == GrColorPackRGBA(0, 255, 0, 255)); // Green. } else if (expectedCrossProduct < 0) { REPORTER_ASSERT(reporter, result == GrColorPackRGBA(255, 0, 0, 255)); // Red. } else { REPORTER_ASSERT(reporter, result == GrColorPackRGBA(0, 0, 0, 255)); // Black. } }