|  | /* | 
|  | * 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 "include/core/SkAlphaType.h" | 
|  | #include "include/core/SkBlendMode.h" | 
|  | #include "include/core/SkMatrix.h" | 
|  | #include "include/core/SkRect.h" | 
|  | #include "include/core/SkRefCnt.h" | 
|  | #include "include/core/SkSamplingOptions.h" | 
|  | #include "include/core/SkSize.h" | 
|  | #include "include/core/SkTypes.h" | 
|  | #include "include/gpu/GpuTypes.h" | 
|  | #include "include/gpu/GrBackendSurface.h" | 
|  | #include "include/gpu/GrDirectContext.h" | 
|  | #include "include/gpu/GrRecordingContext.h" | 
|  | #include "include/gpu/GrTypes.h" | 
|  | #include "include/private/gpu/ganesh/GrTypesPriv.h" | 
|  | #include "src/base/SkArenaAlloc.h" | 
|  | #include "src/gpu/SkBackingFit.h" | 
|  | #include "src/gpu/Swizzle.h" | 
|  | #include "src/gpu/ganesh/GrAppliedClip.h" | 
|  | #include "src/gpu/ganesh/GrCaps.h" | 
|  | #include "src/gpu/ganesh/GrColorSpaceXform.h" | 
|  | #include "src/gpu/ganesh/GrDirectContextPriv.h" | 
|  | #include "src/gpu/ganesh/GrProcessorSet.h" | 
|  | #include "src/gpu/ganesh/GrProxyProvider.h" | 
|  | #include "src/gpu/ganesh/GrRecordingContextPriv.h" | 
|  | #include "src/gpu/ganesh/GrSamplerState.h" | 
|  | #include "src/gpu/ganesh/GrSurfaceProxy.h" | 
|  | #include "src/gpu/ganesh/GrSurfaceProxyView.h" | 
|  | #include "src/gpu/ganesh/geometry/GrQuad.h" | 
|  | #include "src/gpu/ganesh/ops/GrOp.h" | 
|  | #include "src/gpu/ganesh/ops/OpsTask.h" | 
|  | #include "src/gpu/ganesh/ops/TextureOp.h" | 
|  | #include "tests/CtsEnforcement.h" | 
|  | #include "tests/Test.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | struct GrContextOptions; | 
|  |  | 
|  | using namespace skgpu::ganesh; | 
|  |  | 
|  | class OpsTaskTestingAccess { | 
|  | public: | 
|  | typedef skgpu::v1::OpsTask::OpChain OpChain; | 
|  | }; | 
|  |  | 
|  | static void check_chain(OpsTaskTestingAccess::OpChain* chain, SkRect firstRect, SkRect lastRect, | 
|  | int expectedNumOps) { | 
|  | int actualNumOps = 0; | 
|  | for (const auto& op : GrOp::ChainRange<>(chain->head())) { | 
|  | ++actualNumOps; | 
|  |  | 
|  | if (actualNumOps == 1) { | 
|  | SkAssertResult(op.bounds() == firstRect.makeOutset(0.5f, 0.5f)); | 
|  | } else if (actualNumOps == expectedNumOps) { | 
|  | SkAssertResult(op.bounds() == lastRect.makeOutset(0.5f, 0.5f)); | 
|  | } | 
|  | } | 
|  |  | 
|  | SkAssertResult(actualNumOps == expectedNumOps); | 
|  | } | 
|  |  | 
|  | static sk_sp<GrSurfaceProxy> create_proxy(GrRecordingContext* rContext) { | 
|  | const GrCaps* caps = rContext->priv().caps(); | 
|  |  | 
|  | static constexpr SkISize kDimensions = {16, 16}; | 
|  |  | 
|  | const GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888, | 
|  | GrRenderable::kYes); | 
|  | return rContext->priv().proxyProvider()->createProxy(format, | 
|  | kDimensions, | 
|  | GrRenderable::kYes, | 
|  | 1, | 
|  | GrMipmapped::kNo, | 
|  | SkBackingFit::kExact, | 
|  | skgpu::Budgeted::kNo, | 
|  | GrProtected::kNo, | 
|  | /*label=*/"TextureOpTest", | 
|  | GrInternalSurfaceFlags::kNone); | 
|  | } | 
|  |  | 
|  | static GrOp::Owner create_op(GrDirectContext* dContext, SkRect rect, | 
|  | const GrSurfaceProxyView& proxyView, bool isAA) { | 
|  | DrawQuad quad; | 
|  |  | 
|  | quad.fDevice = GrQuad::MakeFromRect(rect.makeOutset(0.5f, 0.5f),  SkMatrix::I()); | 
|  | quad.fLocal = GrQuad(rect); | 
|  | quad.fEdgeFlags = isAA ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone; | 
|  |  | 
|  | return TextureOp::Make(dContext, | 
|  | proxyView, | 
|  | kPremul_SkAlphaType, | 
|  | nullptr, | 
|  | GrSamplerState::Filter::kNearest, | 
|  | GrSamplerState::MipmapMode::kNone, | 
|  | {1.f, 1.f, 1.f, 1.f}, | 
|  | TextureOp::Saturate::kYes, | 
|  | SkBlendMode::kSrcOver, | 
|  | isAA ? GrAAType::kCoverage | 
|  | : GrAAType::kNone, | 
|  | &quad, | 
|  | nullptr); | 
|  | } | 
|  |  | 
|  | // This unit test exercises the crbug.com/1112259 case. | 
|  | DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(TextureOpTest, reporter, ctxInfo, CtsEnforcement::kNever) { | 
|  | GrDirectContext* dContext = ctxInfo.directContext(); | 
|  | const GrCaps* caps = dContext->priv().caps(); | 
|  | SkArenaAlloc arena{nullptr, 0, 1024}; | 
|  | auto auditTrail = dContext->priv().auditTrail(); | 
|  |  | 
|  | if (!caps->dynamicStateArrayGeometryProcessorTextureSupport()) { | 
|  | // This test requires chaining | 
|  | return; | 
|  | } | 
|  |  | 
|  | GrSurfaceProxyView proxyViewA(create_proxy(dContext), | 
|  | kTopLeft_GrSurfaceOrigin, | 
|  | skgpu::Swizzle::RGBA()); | 
|  | GrSurfaceProxyView proxyViewB(create_proxy(dContext), | 
|  | kTopLeft_GrSurfaceOrigin, | 
|  | skgpu::Swizzle::RGBA()); | 
|  | GrSurfaceProxyView proxyViewC(create_proxy(dContext), | 
|  | kTopLeft_GrSurfaceOrigin, | 
|  | skgpu::Swizzle::RGBA()); | 
|  |  | 
|  | static const SkRect kOpARect{  0,  0, 16, 16 }; | 
|  | static const SkRect kOpBRect{ 32,  0, 48, 16 }; | 
|  | static const SkRect kOpCRect{  0, 32, 16, 48 }; | 
|  | static const SkRect kOpDRect{ 32, 32, 48, 48 }; | 
|  |  | 
|  | // opA & opB can chain together but can't merge bc they have different proxies | 
|  | GrOp::Owner opA = create_op(dContext, kOpARect, proxyViewA, false); | 
|  | GrOp::Owner opB = create_op(dContext, kOpBRect, proxyViewB, false); | 
|  |  | 
|  | GrAppliedClip noClip = GrAppliedClip::Disabled(); | 
|  | OpsTaskTestingAccess::OpChain chain1(std::move(opA), GrProcessorSet::EmptySetAnalysis(), | 
|  | &noClip, nullptr); | 
|  | chain1.appendOp(std::move(opB), GrProcessorSet::EmptySetAnalysis(), nullptr, &noClip, *caps, | 
|  | &arena, dContext->priv().auditTrail()); | 
|  | check_chain(&chain1, kOpARect, kOpBRect, 2); | 
|  |  | 
|  | // opC & opD can also chain together but can't merge (bc, again, they have different | 
|  | // proxies). Note, however, that opA and opD do share a proxy so can be merged if opA's | 
|  | // anti-aliasing is upgraded to coverage. | 
|  | GrOp::Owner opC = create_op(dContext, kOpCRect, proxyViewC, true); | 
|  | GrOp::Owner opD = create_op(dContext, kOpDRect, proxyViewA, true); | 
|  |  | 
|  | OpsTaskTestingAccess::OpChain chain2(std::move(opC), GrProcessorSet::EmptySetAnalysis(), | 
|  | &noClip, nullptr); | 
|  | chain2.appendOp(std::move(opD), GrProcessorSet::EmptySetAnalysis(), nullptr, &noClip, *caps, | 
|  | &arena, auditTrail); | 
|  | check_chain(&chain2, kOpCRect, kOpDRect, 2); | 
|  |  | 
|  | // opA and opD, now in separate chains, can merge when the two chains are combined while | 
|  | // opB and opC can still only chain. | 
|  | chain1.prependChain(&chain2, *caps, &arena, auditTrail); | 
|  |  | 
|  | // We should end up with the chain | 
|  | //   opC - opD/opA - opB | 
|  | check_chain(&chain1, kOpCRect, kOpBRect, 3); | 
|  |  | 
|  | chain1.deleteOps(); | 
|  | } |