| /* | |
| * Copyright 2015 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/ganesh/GrProcessorUnitTest.h" | |
| #include <memory> | |
| #include "include/gpu/GrRecordingContext.h" | |
| #include "src/gpu/ganesh/GrFragmentProcessor.h" | |
| #include "src/gpu/ganesh/GrRecordingContextPriv.h" | |
| #include "src/gpu/ganesh/GrSurfaceProxyView.h" | |
| #if GR_TEST_UTILS | |
| class GrGeometryProcessor; | |
| GrProcessorTestData::GrProcessorTestData(SkRandom* random, GrRecordingContext* context, | |
| int maxTreeDepth, int numViews, const ViewInfo views[]) | |
| : GrProcessorTestData(random, context, maxTreeDepth, numViews, views, | |
| /*inputFP=*/nullptr) {} | |
| GrProcessorTestData::GrProcessorTestData(SkRandom* random, GrRecordingContext* context, | |
| int maxTreeDepth, int numViews, const ViewInfo views[], | |
| std::unique_ptr<GrFragmentProcessor> inputFP) | |
| : fRandom(random) | |
| , fMaxTreeDepth(maxTreeDepth) | |
| , fContext(context) | |
| , fInputFP(std::move(inputFP)) { | |
| fViews.reset(views, numViews); | |
| fArena = std::make_unique<SkArenaAlloc>(1000); | |
| } | |
| GrProcessorTestData::~GrProcessorTestData() {} | |
| GrProxyProvider* GrProcessorTestData::proxyProvider() { return fContext->priv().proxyProvider(); } | |
| const GrCaps* GrProcessorTestData::caps() { return fContext->priv().caps(); } | |
| std::unique_ptr<GrFragmentProcessor> GrProcessorTestData::inputFP() { | |
| if (fCurrentTreeDepth == 0) { | |
| // At the top level of the tree, provide the input FP from the test data. | |
| return fInputFP ? fInputFP->clone() : nullptr; | |
| } else { | |
| // At deeper levels of recursion, synthesize a random input. | |
| return GrProcessorUnitTest::MakeChildFP(this); | |
| } | |
| } | |
| GrProcessorTestData::ViewInfo GrProcessorTestData::randomView() { | |
| SkASSERT(!fViews.empty()); | |
| return fViews[fRandom->nextULessThan(fViews.size())]; | |
| } | |
| GrProcessorTestData::ViewInfo GrProcessorTestData::randomAlphaOnlyView() { | |
| int numAlphaOnly = 0; | |
| for (const auto& [v, ct, at] : fViews) { | |
| if (GrColorTypeIsAlphaOnly(ct)) { | |
| ++numAlphaOnly; | |
| } | |
| } | |
| SkASSERT(numAlphaOnly); | |
| int idx = fRandom->nextULessThan(numAlphaOnly); | |
| for (const auto& [v, ct, at] : fViews) { | |
| if (GrColorTypeIsAlphaOnly(ct) && !idx--) { | |
| return {v, ct, at}; | |
| } | |
| } | |
| SkUNREACHABLE; | |
| } | |
| template <class ProcessorSmartPtr> | |
| GrProcessorTestFactory<ProcessorSmartPtr>::GrProcessorTestFactory(MakeProc makeProc, | |
| const char* name) | |
| : fMakeProc(makeProc), fName(name) { | |
| GetFactories()->push_back(this); | |
| } | |
| template <class ProcessorSmartPtr> | |
| ProcessorSmartPtr GrProcessorTestFactory<ProcessorSmartPtr>::Make(GrProcessorTestData* data) { | |
| VerifyFactoryCount(); | |
| if (GetFactories()->size() == 0) { | |
| return nullptr; | |
| } | |
| uint32_t idx = data->fRandom->nextULessThan(GetFactories()->size()); | |
| return MakeIdx(idx, data); | |
| } | |
| template <class ProcessorSmartPtr> | |
| ProcessorSmartPtr GrProcessorTestFactory<ProcessorSmartPtr>::MakeIdx(int idx, | |
| GrProcessorTestData* data) { | |
| SkASSERT(idx < GetFactories()->size()); | |
| GrProcessorTestFactory<ProcessorSmartPtr>* factory = (*GetFactories())[idx]; | |
| ProcessorSmartPtr processor = factory->fMakeProc(data); | |
| if (processor == nullptr) { | |
| SK_ABORT("%s: TestCreate returned null", factory->fName.c_str()); | |
| } | |
| return processor; | |
| } | |
| template <class ProcessorSmartPtr> | |
| int GrProcessorTestFactory<ProcessorSmartPtr>::Count() { | |
| return GetFactories()->size(); | |
| } | |
| GrXPFactoryTestFactory::GrXPFactoryTestFactory(GetFn* getProc) : fGetProc(getProc) { | |
| GetFactories()->push_back(this); | |
| } | |
| const GrXPFactory* GrXPFactoryTestFactory::Get(GrProcessorTestData* data) { | |
| VerifyFactoryCount(); | |
| if (GetFactories()->size() == 0) { | |
| return nullptr; | |
| } | |
| uint32_t idx = data->fRandom->nextRangeU(0, GetFactories()->size() - 1); | |
| const GrXPFactory* xpf = (*GetFactories())[idx]->fGetProc(data); | |
| SkASSERT(xpf); | |
| return xpf; | |
| } | |
| /* | |
| * Originally these were both in the processor unit test header, but then it seemed to cause linker | |
| * problems on android. | |
| */ | |
| template <> | |
| SkTArray<GrFragmentProcessorTestFactory*, true>* GrFragmentProcessorTestFactory::GetFactories() { | |
| static SkTArray<GrFragmentProcessorTestFactory*, true> gFactories; | |
| return &gFactories; | |
| } | |
| template <> | |
| SkTArray<GrGeometryProcessorTestFactory*, true>* GrGeometryProcessorTestFactory::GetFactories() { | |
| static SkTArray<GrGeometryProcessorTestFactory*, true> gFactories; | |
| return &gFactories; | |
| } | |
| SkTArray<GrXPFactoryTestFactory*, true>* GrXPFactoryTestFactory::GetFactories() { | |
| static SkTArray<GrXPFactoryTestFactory*, true> gFactories; | |
| return &gFactories; | |
| } | |
| /* | |
| * To ensure we always have successful static initialization, before creating from the factories | |
| * we verify the count is as expected. If a new factory is added, then these numbers must be | |
| * manually adjusted. | |
| */ | |
| static constexpr int kFPFactoryCount = 16; | |
| static constexpr int kGPFactoryCount = 14; | |
| static constexpr int kXPFactoryCount = 4; | |
| template <> void GrFragmentProcessorTestFactory::VerifyFactoryCount() { | |
| if (kFPFactoryCount != GetFactories()->size()) { | |
| SkDebugf("\nExpected %d fragment processor factories, found %d.\n", kFPFactoryCount, | |
| GetFactories()->size()); | |
| SK_ABORT("Wrong number of fragment processor factories!"); | |
| } | |
| } | |
| template <> void GrGeometryProcessorTestFactory::VerifyFactoryCount() { | |
| if (kGPFactoryCount != GetFactories()->size()) { | |
| SkDebugf("\nExpected %d geometry processor factories, found %d.\n", kGPFactoryCount, | |
| GetFactories()->size()); | |
| SK_ABORT("Wrong number of geometry processor factories!"); | |
| } | |
| } | |
| void GrXPFactoryTestFactory::VerifyFactoryCount() { | |
| if (kXPFactoryCount != GetFactories()->size()) { | |
| SkDebugf("\nExpected %d xp factory factories, found %d.\n", kXPFactoryCount, | |
| GetFactories()->size()); | |
| SK_ABORT("Wrong number of xp factory factories!"); | |
| } | |
| } | |
| std::unique_ptr<GrFragmentProcessor> GrProcessorUnitTest::MakeChildFP(GrProcessorTestData* data) { | |
| std::unique_ptr<GrFragmentProcessor> fp; | |
| ++data->fCurrentTreeDepth; | |
| if (data->fCurrentTreeDepth > data->fMaxTreeDepth) { | |
| // We've gone too deep, but we can't necessarily return null without risking an assertion. | |
| // Instead, return a known-simple zero-child FP. This limits the recursion, and the | |
| // generated FP will be rejected by the numNonNullChildProcessors check below. | |
| fp = GrFragmentProcessor::MakeColor(SK_PMColor4fTRANSPARENT); | |
| } else { | |
| for (;;) { | |
| fp = GrFragmentProcessorTestFactory::Make(data); | |
| SkASSERT(fp); | |
| // If our tree has already reached its max depth, we must reject FPs that have children. | |
| if (data->fCurrentTreeDepth < data->fMaxTreeDepth || | |
| fp->numNonNullChildProcessors() == 0) { | |
| break; | |
| } | |
| } | |
| } | |
| --data->fCurrentTreeDepth; | |
| return fp; | |
| } | |
| std::unique_ptr<GrFragmentProcessor> GrProcessorUnitTest::MakeOptionalChildFP( | |
| GrProcessorTestData* data) { | |
| return data->fRandom->nextBool() ? MakeChildFP(data) : nullptr; | |
| } | |
| template class GrProcessorTestFactory<GrGeometryProcessor*>; | |
| template class GrProcessorTestFactory<std::unique_ptr<GrFragmentProcessor>>; | |
| #endif |