• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google Inc.
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 "gm/gm.h"
9 #include "include/core/SkBlendMode.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkSize.h"
16 #include "include/core/SkString.h"
17 #include "include/core/SkTypes.h"
18 #include "include/gpu/GrContext.h"
19 #include "include/gpu/GrTypes.h"
20 #include "include/private/GrRecordingContext.h"
21 #include "include/private/GrTypesPriv.h"
22 #include "include/private/SkColorData.h"
23 #include "src/gpu/GrBuffer.h"
24 #include "src/gpu/GrCaps.h"
25 #include "src/gpu/GrClip.h"
26 #include "src/gpu/GrColorSpaceXform.h"
27 #include "src/gpu/GrContextPriv.h"
28 #include "src/gpu/GrGeometryProcessor.h"
29 #include "src/gpu/GrMemoryPool.h"
30 #include "src/gpu/GrMesh.h"
31 #include "src/gpu/GrOpFlushState.h"
32 #include "src/gpu/GrOpsRenderPass.h"
33 #include "src/gpu/GrPaint.h"
34 #include "src/gpu/GrPipeline.h"
35 #include "src/gpu/GrPrimitiveProcessor.h"
36 #include "src/gpu/GrProcessor.h"
37 #include "src/gpu/GrProcessorSet.h"
38 #include "src/gpu/GrRecordingContextPriv.h"
39 #include "src/gpu/GrRenderTargetContext.h"
40 #include "src/gpu/GrRenderTargetContextPriv.h"
41 #include "src/gpu/GrSamplerState.h"
42 #include "src/gpu/GrShaderCaps.h"
43 #include "src/gpu/GrShaderVar.h"
44 #include "src/gpu/GrSurfaceProxy.h"
45 #include "src/gpu/GrTextureProxy.h"
46 #include "src/gpu/GrUserStencilSettings.h"
47 #include "src/gpu/effects/GrPorterDuffXferProcessor.h"
48 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
49 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
50 #include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
51 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
52 #include "src/gpu/glsl/GrGLSLVarying.h"
53 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
54 #include "src/gpu/ops/GrDrawOp.h"
55 #include "src/gpu/ops/GrOp.h"
56 #include "tools/gpu/ProxyUtils.h"
57 
58 #include <memory>
59 #include <utility>
60 
61 class GrAppliedClip;
62 class GrGLSLProgramDataManager;
63 
64 namespace skiagm {
65 
66 enum class GradType : bool {
67     kHW,
68     kSW
69 };
70 
71 /**
72  * This test ensures that the shaderBuilder's sample offsets and sample mask are correlated with
73  * actual HW sample locations. It does so by drawing pseudo-random subpixel boxes, and only turning
74  * off the samples whose locations fall inside the boxes.
75  */
76 class SampleLocationsGM : public GpuGM {
77 public:
SampleLocationsGM(GradType gradType,GrSurfaceOrigin origin)78     SampleLocationsGM(GradType gradType, GrSurfaceOrigin origin)
79             : fGradType(gradType)
80             , fOrigin(origin) {}
81 
82 private:
onShortName()83     SkString onShortName() override {
84         return SkStringPrintf("samplelocations%s%s",
85                               (GradType::kHW == fGradType) ? "_hwgrad" : "_swgrad",
86                               (kTopLeft_GrSurfaceOrigin == fOrigin) ? "_topleft" : "_botleft");
87     }
88 
onISize()89     SkISize onISize() override { return SkISize::Make(200, 200); }
90     DrawResult onDraw(GrContext*, GrRenderTargetContext*, SkCanvas*, SkString* errorMsg) override;
91 
92     const GradType fGradType;
93     const GrSurfaceOrigin fOrigin;
94 };
95 
96 ////////////////////////////////////////////////////////////////////////////////////////////////////
97 // SkSL code.
98 
99 class SampleLocationsTestProcessor : public GrGeometryProcessor {
100 public:
Make(SkArenaAlloc * arena,GradType gradType)101     static GrGeometryProcessor* Make(SkArenaAlloc* arena, GradType gradType) {
102         return arena->make<SampleLocationsTestProcessor>(gradType);
103     }
104 
name() const105     const char* name() const override { return "SampleLocationsTestProcessor"; }
106 
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const107     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
108         b->add32((uint32_t)fGradType);
109     }
110 
111     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
112 
113 private:
114     friend class ::SkArenaAlloc; // for access to ctor
115 
SampleLocationsTestProcessor(GradType gradType)116     SampleLocationsTestProcessor(GradType gradType)
117             : GrGeometryProcessor(kSampleLocationsTestProcessor_ClassID)
118             , fGradType(gradType) {
119         this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
120     }
121 
122     const GradType fGradType;
123 
124     class Impl;
125 
126     typedef GrGeometryProcessor INHERITED;
127 };
128 
129 class SampleLocationsTestProcessor::Impl : public GrGLSLGeometryProcessor {
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)130     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
131         const auto& proc = args.fGP.cast<SampleLocationsTestProcessor>();
132         auto* v = args.fVertBuilder;
133         auto* f = args.fFragBuilder;
134 
135         GrGLSLVarying coord(kFloat2_GrSLType);
136         GrGLSLVarying grad(kFloat2_GrSLType);
137         args.fVaryingHandler->addVarying("coord", &coord);
138         if (GradType::kSW == proc.fGradType) {
139             args.fVaryingHandler->addVarying("grad", &grad);
140         }
141 
142         // Pixel grid.
143         v->codeAppendf("int x = sk_InstanceID %% 200;");
144         v->codeAppendf("int y = sk_InstanceID / 200;");
145 
146         // Create pseudo-random rectangles inside a 16x16 subpixel grid. This works out nicely
147         // because there are 17 positions on the grid (including both edges), and 17 is a great
148         // prime number for generating pseudo-random numbers.
149         v->codeAppendf("int ileft = (sk_InstanceID*929) %% 17;");
150         v->codeAppendf("int iright = ileft + 1 + ((sk_InstanceID*1637) %% (17 - ileft));");
151         v->codeAppendf("int itop = (sk_InstanceID*313) %% 17;");
152         v->codeAppendf("int ibot = itop + 1 + ((sk_InstanceID*1901) %% (17 - itop));");
153 
154         // Outset (or inset) the rectangle, for the very likely scenario that samples fall on exact
155         // 16ths of a pixel. GL_SUBPIXEL_BITS is allowed to be as low as 4, so try not to let the
156         // outset value to get too small.
157         v->codeAppendf("float outset = 1/32.0;");
158         v->codeAppendf("outset = (0 == (x + y) %% 2) ? -outset : +outset;");
159         v->codeAppendf("float l = ileft/16.0 - outset;");
160         v->codeAppendf("float r = iright/16.0 + outset;");
161         v->codeAppendf("float t = itop/16.0 - outset;");
162         v->codeAppendf("float b = ibot/16.0 + outset;");
163 
164         v->codeAppendf("float2 vertexpos;");
165         v->codeAppendf("vertexpos.x = float(x) + ((0 == (sk_VertexID %% 2)) ? l : r);");
166         v->codeAppendf("vertexpos.y = float(y) + ((0 == (sk_VertexID / 2)) ? t : b);");
167         gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
168 
169         v->codeAppendf("%s.x = (0 == (sk_VertexID %% 2)) ? -1 : +1;", coord.vsOut());
170         v->codeAppendf("%s.y = (0 == (sk_VertexID / 2)) ? -1 : +1;", coord.vsOut());
171         if (GradType::kSW == proc.fGradType) {
172             v->codeAppendf("%s = 2/float2(r - l, b - t);", grad.vsOut());
173         }
174 
175         // Fragment shader: Output RED.
176         f->codeAppendf("%s = half4(1,0,0,1);", args.fOutputColor);
177         f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
178 
179         // Now turn off all the samples inside our sub-rectangle. As long as the shaderBuilder's
180         // sample offsets and sample mask are correlated with actual HW sample locations, no red
181         // will bleed through.
182         f->codeAppendf("for (int i = 0; i < %i; ++i) {",
183                        f->getProgramBuilder()->effectiveSampleCnt());
184         if (GradType::kHW == proc.fGradType) {
185             f->codeAppendf("float2x2 grad = float2x2(dFdx(%s), dFdy(%s));",
186                            coord.fsIn(), coord.fsIn());
187         } else {
188             f->codeAppendf("float2x2 grad = float2x2(%s.x, 0, 0, %s.y);", grad.fsIn(), grad.fsIn());
189         }
190         f->codeAppendf(    "float2 samplecoord = %s[i] * grad + %s;",
191                            f->sampleOffsets(), coord.fsIn());
192         f->codeAppendf(    "if (all(lessThanEqual(abs(samplecoord), float2(1)))) {");
193         f->maskOffMultisampleCoverage(
194                 "~(1 << i)", GrGLSLFPFragmentBuilder::ScopeFlags::kInsideLoop);
195         f->codeAppendf(    "}");
196         f->codeAppendf("}");
197     }
198 
setData(const GrGLSLProgramDataManager &,const GrPrimitiveProcessor &,const CoordTransformRange &)199     void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
200                  const CoordTransformRange&) override {}
201 };
202 
createGLSLInstance(const GrShaderCaps &) const203 GrGLSLPrimitiveProcessor* SampleLocationsTestProcessor::createGLSLInstance(
204         const GrShaderCaps&) const {
205     return new Impl();
206 }
207 
208 ////////////////////////////////////////////////////////////////////////////////////////////////////
209 // Draw Op.
210 
211 static constexpr GrUserStencilSettings gStencilWrite(
212     GrUserStencilSettings::StaticInit<
213         0x0001,
214         GrUserStencilTest::kAlways,
215         0xffff,
216         GrUserStencilOp::kReplace,
217         GrUserStencilOp::kKeep,
218         0xffff>()
219 );
220 
221 class SampleLocationsTestOp : public GrDrawOp {
222 public:
223     DEFINE_OP_CLASS_ID
224 
Make(GrRecordingContext * ctx,const SkMatrix & viewMatrix,GradType gradType)225     static std::unique_ptr<GrDrawOp> Make(
226             GrRecordingContext* ctx, const SkMatrix& viewMatrix, GradType gradType) {
227         GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
228         return pool->allocate<SampleLocationsTestOp>(gradType);
229     }
230 
231 private:
SampleLocationsTestOp(GradType gradType)232     SampleLocationsTestOp(GradType gradType) : GrDrawOp(ClassID()), fGradType(gradType) {
233         this->setBounds(SkRect::MakeIWH(200, 200), HasAABloat::kNo, IsHairline::kNo);
234     }
235 
name() const236     const char* name() const override { return "SampleLocationsTestOp"; }
fixedFunctionFlags() const237     FixedFunctionFlags fixedFunctionFlags() const override {
238         return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
239     }
finalize(const GrCaps &,const GrAppliedClip *,bool hasMixedSampledCoverage,GrClampType)240     GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
241                                       bool hasMixedSampledCoverage, GrClampType) override {
242         return GrProcessorSet::EmptySetAnalysis();
243     }
244 
onPrePrepare(GrRecordingContext * context,const GrSurfaceProxyView * dstView,GrAppliedClip * clip,const GrXferProcessor::DstProxyView & dstProxyView)245     void onPrePrepare(GrRecordingContext* context,
246                       const GrSurfaceProxyView* dstView,
247                       GrAppliedClip* clip,
248                       const GrXferProcessor::DstProxyView& dstProxyView) final {
249         // We're going to create the GrProgramInfo (and the GrPipeline and geometry processor
250         // it relies on) in the DDL-record-time arena.
251         SkArenaAlloc* arena = context->priv().recordTimeAllocator();
252 
253         // This is equivalent to a GrOpFlushState::detachAppliedClip
254         GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip();
255 
256         GrGeometryProcessor* geomProc = SampleLocationsTestProcessor::Make(arena, fGradType);
257 
258         fProgramInfo = sk_gpu_test::CreateProgramInfo(context->priv().caps(), arena, dstView,
259                                                       std::move(appliedClip), dstProxyView,
260                                                       geomProc, SkBlendMode::kSrcOver,
261                                                       GrPrimitiveType::kTriangleStrip,
262                                                       GrPipeline::InputFlags::kHWAntialias,
263                                                       &gStencilWrite);
264     }
265 
onPrepare(GrOpFlushState *)266     void onPrepare(GrOpFlushState*) final {}
267 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)268     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) final {
269 
270         if (!fProgramInfo) {
271             auto geomProc = SampleLocationsTestProcessor::Make(flushState->allocator(),
272                                                                fGradType);
273 
274             fProgramInfo = sk_gpu_test::CreateProgramInfo(&flushState->caps(),
275                                                           flushState->allocator(),
276                                                           flushState->view(),
277                                                           flushState->detachAppliedClip(),
278                                                           flushState->dstProxyView(),
279                                                           geomProc, SkBlendMode::kSrcOver,
280                                                           GrPrimitiveType::kTriangleStrip,
281                                                           GrPipeline::InputFlags::kHWAntialias,
282                                                           &gStencilWrite);
283         }
284 
285         GrMesh mesh;
286         mesh.setInstanced(nullptr, 200*200, 0, 4);
287 
288         flushState->opsRenderPass()->bindPipeline(*fProgramInfo, SkRect::MakeIWH(200, 200));
289         flushState->opsRenderPass()->drawMeshes(*fProgramInfo, &mesh, 1);
290     }
291 
292     const GradType fGradType;
293 
294     // The program info (and both the GrPipeline and GrPrimitiveProcessor it relies on), when
295     // allocated, are allocated in either the ddl-record-time or flush-time arena. It is the
296     // arena's job to free up their memory so we just have a bare programInfo pointer here. We
297     // don't even store the GrPipeline and GrPrimitiveProcessor pointers here bc they are
298     // guaranteed to have the same lifetime as the program info.
299     GrProgramInfo*  fProgramInfo = nullptr;
300 
301     friend class ::GrOpMemoryPool; // for ctor
302 
303     typedef GrDrawOp INHERITED;
304 };
305 
306 ////////////////////////////////////////////////////////////////////////////////////////////////////
307 // Test.
308 
onDraw(GrContext * ctx,GrRenderTargetContext * rtc,SkCanvas * canvas,SkString * errorMsg)309 DrawResult SampleLocationsGM::onDraw(
310         GrContext* ctx, GrRenderTargetContext* rtc, SkCanvas* canvas, SkString* errorMsg) {
311     if (!ctx->priv().caps()->sampleLocationsSupport()) {
312         *errorMsg = "Requires support for sample locations.";
313         return DrawResult::kSkip;
314     }
315     if (!ctx->priv().caps()->shaderCaps()->sampleMaskSupport()) {
316         *errorMsg = "Requires support for sample mask.";
317         return DrawResult::kSkip;
318     }
319     if (rtc->numSamples() <= 1 && !ctx->priv().caps()->mixedSamplesSupport()) {
320         *errorMsg = "MSAA and mixed samples only.";
321         return DrawResult::kSkip;
322     }
323 
324     auto offscreenRTC = GrRenderTargetContext::Make(
325             ctx, rtc->colorInfo().colorType(), nullptr, SkBackingFit::kExact, {200, 200},
326             rtc->numSamples(), GrMipMapped::kNo, GrProtected::kNo, fOrigin);
327     if (!offscreenRTC) {
328         *errorMsg = "Failed to create offscreen render target.";
329         return DrawResult::kFail;
330     }
331     if (offscreenRTC->numSamples() <= 1 &&
332         !offscreenRTC->asRenderTargetProxy()->canUseMixedSamples(*ctx->priv().caps())) {
333         *errorMsg = "MSAA and mixed samples only.";
334         return DrawResult::kSkip;
335     }
336 
337     static constexpr GrUserStencilSettings kStencilCover(
338         GrUserStencilSettings::StaticInit<
339             0x0000,
340             GrUserStencilTest::kNotEqual,
341             0xffff,
342             GrUserStencilOp::kZero,
343             GrUserStencilOp::kKeep,
344             0xffff>()
345     );
346 
347     offscreenRTC->clear(nullptr, {0,1,0,1}, GrRenderTargetContext::CanClearFullscreen::kYes);
348 
349     // Stencil.
350     offscreenRTC->priv().testingOnly_addDrawOp(
351             SampleLocationsTestOp::Make(ctx, canvas->getTotalMatrix(), fGradType));
352 
353     // Cover.
354     GrPaint coverPaint;
355     coverPaint.setColor4f({1,0,0,1});
356     coverPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrcOver));
357     rtc->priv().stencilRect(GrNoClip(), &kStencilCover, std::move(coverPaint), GrAA::kNo,
358                             SkMatrix::I(), SkRect::MakeWH(200, 200));
359 
360     // Copy offscreen texture to canvas.
361     rtc->drawTexture(GrNoClip(), offscreenRTC->readSurfaceView(),
362                      offscreenRTC->colorInfo().alphaType(),
363                      GrSamplerState::Filter::kNearest, SkBlendMode::kSrc, SK_PMColor4fWHITE,
364                      {0,0,200,200}, {0,0,200,200}, GrAA::kNo, GrQuadAAFlags::kNone,
365                      SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
366                      nullptr);
367 
368     return skiagm::DrawResult::kOk;
369 }
370 
371 DEF_GM( return new SampleLocationsGM(GradType::kHW, kTopLeft_GrSurfaceOrigin); )
372 DEF_GM( return new SampleLocationsGM(GradType::kHW, kBottomLeft_GrSurfaceOrigin); )
373 DEF_GM( return new SampleLocationsGM(GradType::kSW, kTopLeft_GrSurfaceOrigin); )
374 DEF_GM( return new SampleLocationsGM(GradType::kSW, kBottomLeft_GrSurfaceOrigin); )
375 
376 }
377