John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2020 Google LLC |
| 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 "tests/Test.h" |
| 9 | |
Brian Salomon | 4895946 | 2021-08-11 13:01:06 -0400 | [diff] [blame] | 10 | #include "src/gpu/GrFragmentProcessor.h" |
Robert Phillips | 550de7f | 2021-07-06 16:28:52 -0400 | [diff] [blame] | 11 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 12 | #include "src/gpu/v1/SurfaceDrawContext_v1.h" |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 13 | |
Brian Salomon | eebe735 | 2020-12-09 16:37:04 -0500 | [diff] [blame] | 14 | static void run_test(skiatest::Reporter*, GrDirectContext*, |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 15 | skgpu::v1::SurfaceDrawContext*, SkVector a, |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 16 | SkVector b, float expectedCrossProduct); |
| 17 | |
| 18 | // This is a GPU test that ensures the SkSL 2d cross() intrinsic returns the correct sign (negative, |
| 19 | // positive, or zero). |
| 20 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkSLCross, reporter, ctxInfo) { |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 21 | GrDirectContext* dContext = ctxInfo.directContext(); |
| 22 | auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr, |
| 23 | SkBackingFit::kExact, {1, 1}, SkSurfaceProps()); |
| 24 | if (!sdc) { |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 25 | ERRORF(reporter, "could not create render target context."); |
| 26 | return; |
| 27 | } |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 28 | run_test(reporter, dContext, sdc.get(), {3,4}, {5,6}, -2); // Negative. |
| 29 | run_test(reporter, dContext, sdc.get(), {3,4}, {-5,-6}, 2); // Positive. |
| 30 | run_test(reporter, dContext, sdc.get(), {0, 2.287f}, {0, -7.741f}, 0); // Zero. |
| 31 | run_test(reporter, dContext, sdc.get(), {62.17f, 0}, {-43.49f, 0}, 0); // Zero. |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 32 | } |
| 33 | |
| 34 | namespace { |
| 35 | |
| 36 | // Outputs: |
| 37 | // Green if cross(a,b) > 0 |
| 38 | // Red if cross(a,b) < 0 |
| 39 | // Black if cross(a,b) == 0 |
| 40 | class VisualizeCrossProductSignFP : public GrFragmentProcessor { |
| 41 | public: |
| 42 | VisualizeCrossProductSignFP(SkVector a, SkVector b) |
| 43 | : GrFragmentProcessor(kTestFP_ClassID, kPreservesOpaqueInput_OptimizationFlag) |
| 44 | , fA(a), fB(b) { |
| 45 | } |
| 46 | |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 47 | const char* name() const override { return "VisualizeCrossProductSignFP"; } |
Brian Salomon | b25560a | 2021-08-10 13:56:13 -0400 | [diff] [blame] | 48 | |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 49 | std::unique_ptr<GrFragmentProcessor> clone() const override { |
| 50 | return std::unique_ptr<GrFragmentProcessor>(new VisualizeCrossProductSignFP(fA, fB)); |
| 51 | } |
Brian Salomon | b25560a | 2021-08-10 13:56:13 -0400 | [diff] [blame] | 52 | |
| 53 | private: |
Brian Salomon | 13b2873 | 2021-08-06 15:33:58 -0400 | [diff] [blame] | 54 | void onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 55 | bool onIsEqual(const GrFragmentProcessor&) const override { return true; } |
| 56 | |
Brian Salomon | b25560a | 2021-08-10 13:56:13 -0400 | [diff] [blame] | 57 | std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override { |
| 58 | class Impl : public ProgramImpl { |
| 59 | public: |
| 60 | void emitCode(EmitArgs& args) override { |
| 61 | auto& fp = args.fFp.cast<VisualizeCrossProductSignFP>(); |
| 62 | const char *a, *b; |
| 63 | fAUniform = args.fUniformHandler->addUniform(&fp, kFragment_GrShaderFlag, |
| 64 | GrSLType::kFloat2_GrSLType, "a", &a); |
| 65 | fBUniform = args.fUniformHandler->addUniform(&fp, kFragment_GrShaderFlag, |
| 66 | GrSLType::kFloat2_GrSLType, "b", &b); |
| 67 | args.fFragBuilder->codeAppendf(R"( |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 68 | float crossProduct = cross(%s, %s); |
| 69 | float2 visualization = clamp(float2(-sign(crossProduct), sign(crossProduct)), |
| 70 | float2(0), float2(1)); |
Brian Salomon | b25560a | 2021-08-10 13:56:13 -0400 | [diff] [blame] | 71 | return half2(visualization).xy01;)", a, b); |
| 72 | } |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 73 | |
Brian Salomon | b25560a | 2021-08-10 13:56:13 -0400 | [diff] [blame] | 74 | private: |
| 75 | void onSetData(const GrGLSLProgramDataManager& pdman, |
| 76 | const GrFragmentProcessor& processor) override { |
| 77 | const auto& fp = processor.cast<VisualizeCrossProductSignFP>(); |
| 78 | pdman.set2f(fAUniform, fp.fA.x(), fp.fA.y()); |
| 79 | pdman.set2f(fBUniform, fp.fB.x(), fp.fB.y()); |
| 80 | } |
| 81 | GrGLSLUniformHandler::UniformHandle fAUniform; |
| 82 | GrGLSLUniformHandler::UniformHandle fBUniform; |
| 83 | }; |
| 84 | |
Brian Salomon | 18ab203 | 2021-02-23 10:07:05 -0500 | [diff] [blame] | 85 | return std::make_unique<Impl>(); |
| 86 | } |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 87 | const SkVector fA, fB; |
| 88 | }; |
| 89 | |
| 90 | } // namespace |
| 91 | |
| 92 | static void run_test(skiatest::Reporter* reporter, GrDirectContext* directContext, |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 93 | skgpu::v1::SurfaceDrawContext* sdc, SkVector a, SkVector b, |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 94 | float expectedCrossProduct) { |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 95 | SkASSERT(sdc->width() == 1); |
| 96 | SkASSERT(sdc->height() == 1); |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 97 | |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 98 | sdc->clear(SkPMColor4f::FromBytes_RGBA(0xbaaaaaad)); |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 99 | |
| 100 | GrPaint crossPaint; |
| 101 | crossPaint.setColor4f(SK_PMColor4fWHITE); |
| 102 | crossPaint.setPorterDuffXPFactory(SkBlendMode::kSrcOver); |
| 103 | crossPaint.setColorFragmentProcessor(std::make_unique<VisualizeCrossProductSignFP>(a, b)); |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 104 | sdc->drawRect(/*clip=*/nullptr, std::move(crossPaint), GrAA::kNo, SkMatrix::I(), |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 105 | SkRect::MakeWH(1,1)); |
| 106 | |
| 107 | GrColor result; |
Brian Salomon | dd4087d | 2020-12-23 20:36:44 -0500 | [diff] [blame] | 108 | GrPixmap resultPM(SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType), |
| 109 | &result, |
| 110 | sizeof(GrColor)); |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 111 | sdc->readPixels(directContext, resultPM, {0, 0}); |
John Stiles | 6f3015a | 2020-10-08 14:55:36 -0400 | [diff] [blame] | 112 | |
| 113 | SkASSERT(expectedCrossProduct == a.cross(b)); |
| 114 | if (expectedCrossProduct > 0) { |
| 115 | REPORTER_ASSERT(reporter, result == GrColorPackRGBA(0, 255, 0, 255)); // Green. |
| 116 | } else if (expectedCrossProduct < 0) { |
| 117 | REPORTER_ASSERT(reporter, result == GrColorPackRGBA(255, 0, 0, 255)); // Red. |
| 118 | } else { |
| 119 | REPORTER_ASSERT(reporter, result == GrColorPackRGBA(0, 0, 0, 255)); // Black. |
| 120 | } |
| 121 | } |