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 "include/gpu/GrDirectContext.h"
9 #include "include/gpu/GrRecordingContext.h"
10 #include "src/gpu/GrColorSpaceXform.h"
11 #include "src/gpu/GrDirectContextPriv.h"
12 #include "src/gpu/GrProxyProvider.h"
13 #include "src/gpu/GrRecordingContextPriv.h"
14 #include "src/gpu/geometry/GrQuad.h"
15 #include "src/gpu/ops/OpsTask.h"
16 #include "src/gpu/ops/TextureOp.h"
17 #include "tests/Test.h"
18
19 class OpsTaskTestingAccess {
20 public:
21 typedef skgpu::v1::OpsTask::OpChain OpChain;
22 };
23
check_chain(OpsTaskTestingAccess::OpChain * chain,SkRect firstRect,SkRect lastRect,int expectedNumOps)24 static void check_chain(OpsTaskTestingAccess::OpChain* chain, SkRect firstRect, SkRect lastRect,
25 int expectedNumOps) {
26 int actualNumOps = 0;
27 for (const auto& op : GrOp::ChainRange<>(chain->head())) {
28 ++actualNumOps;
29
30 if (actualNumOps == 1) {
31 SkAssertResult(op.bounds() == firstRect.makeOutset(0.5f, 0.5f));
32 } else if (actualNumOps == expectedNumOps) {
33 SkAssertResult(op.bounds() == lastRect.makeOutset(0.5f, 0.5f));
34 }
35 }
36
37 SkAssertResult(actualNumOps == expectedNumOps);
38 }
39
create_proxy(GrRecordingContext * rContext)40 static sk_sp<GrSurfaceProxy> create_proxy(GrRecordingContext* rContext) {
41 const GrCaps* caps = rContext->priv().caps();
42
43 static constexpr SkISize kDimensions = {16, 16};
44
45 const GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
46 GrRenderable::kYes);
47 return rContext->priv().proxyProvider()->createProxy(
48 format, kDimensions, GrRenderable::kYes, 1, GrMipmapped::kNo, SkBackingFit::kExact,
49 SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone);
50 }
51
create_op(GrDirectContext * dContext,SkRect rect,const GrSurfaceProxyView & proxyView,bool isAA)52 static GrOp::Owner create_op(GrDirectContext* dContext, SkRect rect,
53 const GrSurfaceProxyView& proxyView, bool isAA) {
54 DrawQuad quad;
55
56 quad.fDevice = GrQuad::MakeFromRect(rect.makeOutset(0.5f, 0.5f), SkMatrix::I());
57 quad.fLocal = GrQuad(rect);
58 quad.fEdgeFlags = isAA ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
59
60 return skgpu::v1::TextureOp::Make(dContext,
61 proxyView,
62 kPremul_SkAlphaType,
63 nullptr,
64 GrSamplerState::Filter::kNearest,
65 GrSamplerState::MipmapMode::kNone,
66 {1.f, 1.f, 1.f, 1.f},
67 skgpu::v1::TextureOp::Saturate::kYes,
68 SkBlendMode::kSrcOver,
69 isAA ? GrAAType::kCoverage
70 : GrAAType::kNone,
71 &quad,
72 nullptr);
73 }
74
75 // This unit test exercises the crbug.com/1112259 case.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureOpTest,reporter,ctxInfo)76 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureOpTest, reporter, ctxInfo) {
77
78 GrDirectContext* dContext = ctxInfo.directContext();
79 const GrCaps* caps = dContext->priv().caps();
80 SkArenaAlloc arena{nullptr, 0, 1024};
81 auto auditTrail = dContext->priv().auditTrail();
82
83 if (!caps->dynamicStateArrayGeometryProcessorTextureSupport()) {
84 // This test requires chaining
85 return;
86 }
87
88 GrSurfaceProxyView proxyViewA(create_proxy(dContext),
89 kTopLeft_GrSurfaceOrigin,
90 GrSwizzle::RGBA());
91 GrSurfaceProxyView proxyViewB(create_proxy(dContext),
92 kTopLeft_GrSurfaceOrigin,
93 GrSwizzle::RGBA());
94 GrSurfaceProxyView proxyViewC(create_proxy(dContext),
95 kTopLeft_GrSurfaceOrigin,
96 GrSwizzle::RGBA());
97
98 static const SkRect kOpARect{ 0, 0, 16, 16 };
99 static const SkRect kOpBRect{ 32, 0, 48, 16 };
100 static const SkRect kOpCRect{ 0, 32, 16, 48 };
101 static const SkRect kOpDRect{ 32, 32, 48, 48 };
102
103 // opA & opB can chain together but can't merge bc they have different proxies
104 GrOp::Owner opA = create_op(dContext, kOpARect, proxyViewA, false);
105 GrOp::Owner opB = create_op(dContext, kOpBRect, proxyViewB, false);
106
107 GrAppliedClip noClip = GrAppliedClip::Disabled();
108 OpsTaskTestingAccess::OpChain chain1(std::move(opA), GrProcessorSet::EmptySetAnalysis(),
109 &noClip, nullptr);
110 chain1.appendOp(std::move(opB), GrProcessorSet::EmptySetAnalysis(), nullptr, &noClip, *caps,
111 &arena, dContext->priv().auditTrail());
112 check_chain(&chain1, kOpARect, kOpBRect, 2);
113
114 // opC & opD can also chain together but can't merge (bc, again, they have different
115 // proxies). Note, however, that opA and opD do share a proxy so can be merged if opA's
116 // anti-aliasing is upgraded to coverage.
117 GrOp::Owner opC = create_op(dContext, kOpCRect, proxyViewC, true);
118 GrOp::Owner opD = create_op(dContext, kOpDRect, proxyViewA, true);
119
120 OpsTaskTestingAccess::OpChain chain2(std::move(opC), GrProcessorSet::EmptySetAnalysis(),
121 &noClip, nullptr);
122 chain2.appendOp(std::move(opD), GrProcessorSet::EmptySetAnalysis(), nullptr, &noClip, *caps,
123 &arena, auditTrail);
124 check_chain(&chain2, kOpCRect, kOpDRect, 2);
125
126 // opA and opD, now in separate chains, can merge when the two chains are combined while
127 // opB and opC can still only chain.
128 chain1.prependChain(&chain2, *caps, &arena, auditTrail);
129
130 // We should end up with the chain
131 // opC - opD/opA - opB
132 check_chain(&chain1, kOpCRect, kOpBRect, 3);
133
134 chain1.deleteOps();
135 }
136