xref: /aosp_15_r20/external/skia/tests/GrGpuBufferTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 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 "include/core/SkBlendMode.h"
9 #include "include/core/SkColor.h"
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkSurfaceProps.h"
15 #include "include/core/SkTypes.h"
16 #include "include/gpu/ganesh/GrDirectContext.h"
17 #include "include/private/SkColorData.h"
18 #include "include/private/base/SkAlign.h"
19 #include "include/private/base/SkTemplates.h"
20 #include "include/private/gpu/ganesh/GrTypesPriv.h"
21 #include "src/core/SkSLTypeShared.h"
22 #include "src/gpu/SkBackingFit.h"
23 #include "src/gpu/ganesh/GrAppliedClip.h"
24 #include "src/gpu/ganesh/GrBuffer.h"
25 #include "src/gpu/ganesh/GrCaps.h"
26 #include "src/gpu/ganesh/GrDirectContextPriv.h"
27 #include "src/gpu/ganesh/GrDrawingManager.h"
28 #include "src/gpu/ganesh/GrGeometryProcessor.h"
29 #include "src/gpu/ganesh/GrGpu.h"
30 #include "src/gpu/ganesh/GrGpuBuffer.h"
31 #include "src/gpu/ganesh/GrImageInfo.h"
32 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
33 #include "src/gpu/ganesh/GrOpFlushState.h"
34 #include "src/gpu/ganesh/GrPipeline.h"
35 #include "src/gpu/ganesh/GrPixmap.h"
36 #include "src/gpu/ganesh/GrProcessorAnalysis.h"
37 #include "src/gpu/ganesh/GrProcessorSet.h"
38 #include "src/gpu/ganesh/GrProgramInfo.h"
39 #include "src/gpu/ganesh/GrResourceProvider.h"
40 #include "src/gpu/ganesh/GrSimpleMesh.h"
41 #include "src/gpu/ganesh/GrUserStencilSettings.h"
42 #include "src/gpu/ganesh/SurfaceDrawContext.h"
43 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
44 #include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
45 #include "src/gpu/ganesh/ops/GrMeshDrawOp.h"
46 #include "src/gpu/ganesh/ops/GrOp.h"
47 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
48 #include "tests/CtsEnforcement.h"
49 #include "tests/Test.h"
50 
51 #include <algorithm>
52 #include <cstring>
53 #include <initializer_list>
54 #include <memory>
55 #include <string_view>
56 #include <utility>
57 
58 class GrDstProxyView;
59 class GrGLSLProgramDataManager;
60 class GrRecordingContext;
61 class GrSurfaceProxyView;
62 class SkArenaAlloc;
63 enum class GrXferBarrierFlags;
64 namespace skgpu { class KeyBuilder; }
65 struct GrContextOptions;
66 struct GrShaderCaps;
67 
68 // Simple op that draws a vertex buffer with float2 positions as green triangles. We use this to
69 // draw GrGpuBuffers to test that the buffer contains the expected values as not all contexts will
70 // support buffer mapping.
71 class TestVertexOp final : public GrMeshDrawOp {
72 public:
Make(GrRecordingContext * context,sk_sp<GrGpuBuffer> buffer,int baseVertex,int vertexCount,const SkRect & bounds)73     static GrOp::Owner Make(GrRecordingContext* context,
74                             sk_sp<GrGpuBuffer> buffer,
75                             int baseVertex,
76                             int vertexCount,
77                             const SkRect& bounds) {
78         return GrOp::Make<TestVertexOp>(context,
79                                         std::move(buffer),
80                                         baseVertex,
81                                         vertexCount,
82                                         bounds);
83     }
84 
name() const85     const char* name() const override { return "TestVertexOp"; }
86 
fixedFunctionFlags() const87     FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
88 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)89     GrProcessorSet::Analysis finalize(const GrCaps& caps,
90                                       const GrAppliedClip* clip,
91                                       GrClampType clampType) override {
92         static constexpr SkPMColor4f kGreen{0, 1, 0, 1};
93         SkPMColor4f color = kGreen;
94         auto analysis = fProcessorSet.finalize(GrProcessorAnalysisColor::Opaque::kYes,
95                                                GrProcessorAnalysisCoverage::kNone,
96                                                clip,
97                                                &GrUserStencilSettings::kUnused,
98                                                caps,
99                                                clampType,
100                                                &color);
101         SkASSERT(color == kGreen);
102         return analysis;
103     }
104 
visitProxies(const GrVisitProxyFunc & func) const105     void visitProxies(const GrVisitProxyFunc& func) const override {
106         if (fProgramInfo) {
107             fProgramInfo->visitFPProxies(func);
108         }
109     }
110 
111 private:
112     DEFINE_OP_CLASS_ID
113 
TestVertexOp(sk_sp<GrGpuBuffer> buffer,int baseVertex,int vertexCount,const SkRect & bounds)114     TestVertexOp(sk_sp<GrGpuBuffer> buffer,
115                  int baseVertex,
116                  int vertexCount,
117                  const SkRect& bounds)
118                  : GrMeshDrawOp(ClassID())
119                  , fBuffer(std::move(buffer))
120                  , fProcessorSet(SkBlendMode::kSrc)
121                  , fBaseVertex(baseVertex)
122                  , fVertexCount(vertexCount) {
123         this->setBounds(bounds, HasAABloat::kNo, GrOp::IsHairline::kNo);
124      }
125 
programInfo()126     GrProgramInfo* programInfo() override { return fProgramInfo; }
127 
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)128     void onCreateProgramInfo(const GrCaps* caps,
129                              SkArenaAlloc* arena,
130                              const GrSurfaceProxyView& writeView,
131                              bool usesMSAASurface,
132                              GrAppliedClip&& appliedClip,
133                              const GrDstProxyView& dstProxyView,
134                              GrXferBarrierFlags renderPassXferBarriers,
135                              GrLoadOp colorLoadOp) override {
136         fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
137                 caps,
138                 arena,
139                 writeView,
140                 usesMSAASurface,
141                 std::move(appliedClip),
142                 dstProxyView,
143                 &fGP,
144                 std::move(fProcessorSet),
145                 GrPrimitiveType::kTriangles,
146                 renderPassXferBarriers,
147                 colorLoadOp,
148                 GrPipeline::InputFlags::kNone);
149     }
150 
151     class GP : public GrGeometryProcessor {
152     public:
GP()153         GP() : GrGeometryProcessor(kTestFP_ClassID) {
154             this->setVertexAttributesWithImplicitOffsets(&kPos, 1);
155         }
156 
name() const157         const char* name() const override { return "TestVertexOp::GP"; }
158 
makeProgramImpl(const GrShaderCaps &) const159         std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
160             class Impl : public ProgramImpl {
161             public:
162                 void setData(const GrGLSLProgramDataManager&,
163                              const GrShaderCaps&,
164                              const GrGeometryProcessor&) override {}
165 
166             private:
167                 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
168                     const auto& gp = args.fGeomProc.cast<GP>();
169                     args.fVaryingHandler->emitAttributes(gp);
170                     args.fFragBuilder->codeAppendf("half4 %s = half4(0, 1, 0, 1);",
171                                                    args.fOutputColor);
172                     args.fFragBuilder->codeAppendf("const half4 %s = half4(1);",
173                                                    args.fOutputCoverage);
174                     WriteOutputPosition(args.fVertBuilder, gpArgs, kPos.name());
175                 }
176 
177                 UniformHandle fLocalMatrixUni;
178             };
179 
180             return std::make_unique<Impl>();
181         }
182 
addToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * builder) const183         void addToKey(const GrShaderCaps &caps, skgpu::KeyBuilder *builder) const override {}
184 
185     private:
186         static constexpr Attribute kPos = {"pos", kFloat2_GrVertexAttribType, SkSLType::kFloat2};
187     };
188 
onPrepareDraws(GrMeshDrawTarget * target)189     void onPrepareDraws(GrMeshDrawTarget* target) override {
190         fMesh = target->allocMesh();
191         fMesh->set(fBuffer, fVertexCount, fBaseVertex);
192     }
193 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)194     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
195         if (!fProgramInfo) {
196             this->createProgramInfo(flushState);
197         }
198 
199         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
200         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
201         flushState->drawMesh(*fMesh);
202     }
203 
204     sk_sp<GrGpuBuffer> fBuffer;
205 
206     GP fGP;
207 
208     GrProcessorSet fProcessorSet;
209 
210     int fBaseVertex;
211     int fVertexCount;
212 
213     GrProgramInfo* fProgramInfo = nullptr;
214     GrSimpleMesh*  fMesh        = nullptr;
215 
216     friend class ::GrOp;
217 };
218 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrGpuBufferTransferTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_U)219 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrGpuBufferTransferTest,
220                                        reporter,
221                                        ctxInfo,
222                                        CtsEnforcement::kApiLevel_U) {
223     if (!ctxInfo.directContext()->priv().caps()->transferFromBufferToBufferSupport()) {
224         return;
225     }
226 
227     GrDirectContext* dc = ctxInfo.directContext();
228 
229     GrDrawingManager* dm = dc->priv().drawingManager();
230 
231     GrResourceProvider* rp = ctxInfo.directContext()->priv().resourceProvider();
232 
233     GrGpu* gpu = ctxInfo.directContext()->priv().getGpu();
234 
235     auto create_cpu_to_gpu_buffer = [&](int baseVertex) {
236         // Ensure any extra vertices are offscreen
237         int totalVertices = baseVertex + 6;
238         auto points = std::make_unique<SkPoint[]>(totalVertices);
239         SkPoint offscreenPt{-10000, -10000};
240         std::fill_n(points.get(), totalVertices, offscreenPt);
241 
242         // set the quad at the desired base vertex
243         static constexpr SkPoint kUnitQuad[] {{0, 0}, {0, 1}, {1, 0},
244                                               {1, 0}, {0, 1}, {1, 1}};
245         std::copy_n(kUnitQuad, 6, points.get() + baseVertex);
246 
247         return rp->createBuffer(points.get(),
248                                 totalVertices*sizeof(SkPoint),
249                                 GrGpuBufferType::kXferCpuToGpu,
250                                 kDynamic_GrAccessPattern);
251     };
252 
253     auto create_vertex_buffer = [&](sk_sp<GrGpuBuffer> srcBuffer,
254                                     int srcBaseVertex,
255                                     int vbBaseVertex,
256                                     bool useTask,
257                                     bool minSizedTransfers) {
258         // make initialization data of offscreen points.
259         int dstVertexCount = vbBaseVertex + 6;
260         auto points = std::make_unique<SkPoint[]>(dstVertexCount);
261         SkPoint offscreenPt{-10000, -10000};
262         std::fill_n(points.get(), dstVertexCount, offscreenPt);
263 
264         sk_sp<GrGpuBuffer> vb = rp->createBuffer(points.get(),
265                                                  dstVertexCount*sizeof(SkPoint),
266                                                  GrGpuBufferType::kVertex,
267                                                  kDynamic_GrAccessPattern);
268 
269         // copy actual quad data from the source buffer to our new vb.
270 
271         static constexpr size_t kTotalSize = 6*sizeof(SkPoint);
272 
273         size_t srcOffset = srcBaseVertex*sizeof(SkPoint);
274         size_t  vbOffset =  vbBaseVertex*sizeof(SkPoint);
275 
276         size_t alignment = gpu->caps()->transferFromBufferToBufferAlignment();
277         SkASSERT(kTotalSize      % alignment == 0);
278         SkASSERT(sizeof(SkPoint) % alignment == 0);
279 
280         if (minSizedTransfers) {
281             for (size_t n = kTotalSize/alignment, i = 0; i < n; ++i) {
282                 if (useTask) {
283                     dm->newBufferTransferTask(srcBuffer,
284                                               srcOffset + i*alignment,
285                                               vb,
286                                               vbOffset + i*alignment,
287                                               alignment);
288                 } else {
289                     gpu->transferFromBufferToBuffer(srcBuffer,
290                                                     srcOffset + i*alignment,
291                                                     vb,
292                                                     vbOffset + i*alignment,
293                                                     alignment);
294                 }
295             }
296         } else if (useTask) {
297             dm->newBufferTransferTask(srcBuffer, srcOffset, vb, vbOffset, kTotalSize);
298         } else {
299             gpu->transferFromBufferToBuffer(srcBuffer, srcOffset, vb, vbOffset, kTotalSize);
300         }
301         return vb;
302     };
303 
304     auto sdc = skgpu::ganesh::SurfaceDrawContext::Make(dc,
305                                                        GrColorType::kRGBA_8888,
306                                                        nullptr,
307                                                        SkBackingFit::kExact,
308                                                        {1, 1},
309                                                        SkSurfaceProps{},
310                                                        std::string_view{});
311     if (!sdc) {
312         ERRORF(reporter, "Could not create draw context");
313         return;
314     }
315 
316     auto pm = GrPixmap::Allocate(sdc->imageInfo().makeColorType(GrColorType::kRGBA_F32));
317 
318     for (bool useTask : {false, true}) {
319         for (bool minSizedTransfers : {false, true}) {
320             for (int srcBaseVertex : {0, 5}) {
321                 auto src = create_cpu_to_gpu_buffer(srcBaseVertex);
322                 if (!src) {
323                     ERRORF(reporter, "Could not create src buffer");
324                     return;
325                 }
326                 for (int vbBaseVertex : {0, 2}) {
327                     auto vb = create_vertex_buffer(src,
328                                                    srcBaseVertex,
329                                                    vbBaseVertex,
330                                                    useTask,
331                                                    minSizedTransfers);
332                     if (!vb) {
333                         ERRORF(reporter, "Could not create vertex buffer");
334                         return;
335                     }
336 
337                     static constexpr SkColor4f kRed{1, 0, 0, 1};
338 
339                     static constexpr SkRect kBounds{0, 0, 1, 1};
340 
341                     sdc->clear(kRed);
342 
343                     sdc->addDrawOp(nullptr, TestVertexOp::Make(dc,
344                                                                vb,
345                                                                vbBaseVertex,
346                                                                /*vertexCount=*/6,
347                                                                kBounds));
348 
349                     auto color = static_cast<SkPMColor4f*>(pm.addr());
350                     *color = kRed.premul();
351                     if (!sdc->readPixels(dc, pm, {0, 0})) {
352                         ERRORF(reporter, "Read back failed.");
353                         return;
354                     }
355 
356                     static constexpr SkPMColor4f kGreen{0, 1, 0, 1};
357 
358                     REPORTER_ASSERT(reporter, *color == kGreen, "src base vertex: %d, "
359                                                                 "vb base vertex: %d, "
360                                                                 "use task: %d, "
361                                                                 "minSizedTransfers: %d",
362                                                                 srcBaseVertex,
363                                                                 vbBaseVertex,
364                                                                 useTask,
365                                                                 minSizedTransfers);
366                 }
367             }
368         }
369     }
370 }
371 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrGpuBufferUpdateDataTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_U)372 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrGpuBufferUpdateDataTest,
373                                        reporter,
374                                        ctxInfo,
375                                        CtsEnforcement::kApiLevel_U) {
376     GrDirectContext* dc = ctxInfo.directContext();
377 
378     GrGpu* gpu = ctxInfo.directContext()->priv().getGpu();
379 
380     static constexpr SkPoint kUnitQuad[] {{0, 0}, {0, 1}, {1, 0},
381                                           {1, 0}, {0, 1}, {1, 1}};
382 
383     auto sdc = skgpu::ganesh::SurfaceDrawContext::Make(dc,
384                                                        GrColorType::kRGBA_8888,
385                                                        nullptr,
386                                                        SkBackingFit::kExact,
387                                                        {1, 1},
388                                                        SkSurfaceProps{},
389                                                        std::string_view{});
390     if (!sdc) {
391         ERRORF(reporter, "Could not create draw context");
392         return;
393     }
394 
395     auto pm = GrPixmap::Allocate(sdc->imageInfo().makeColorType(GrColorType::kRGBA_F32));
396 
397     for (bool piecewise : {false, true}) {
398         size_t alignment = piecewise ? gpu->caps()->bufferUpdateDataPreserveAlignment() : 1;
399         for (size_t offset : {size_t{0}, 4*sizeof(SkPoint), size_t{1}, size_t{27}}) {
400             // For non-discarding updates we may not be able to actually put the data at an
401             // arbitrary offset.
402             if (alignment > 1) {
403                 offset = SkAlignTo(offset, alignment);
404             }
405             for (auto accessPattern : {kStatic_GrAccessPattern,
406                                        //  kStream_GrAccessPattern, GrVkGpu asserts on this for VBs.
407                                        kDynamic_GrAccessPattern}) {
408                 // Go direct to GrGpu to avoid caching/size adjustments at GrResourceProvider level.
409                 // We add an extra size(SkPoint) to ensure that everything fits when we align the
410                 // first point's location in the vb below.
411                 auto vb = gpu->createBuffer(sizeof(kUnitQuad) + offset + sizeof(SkPoint),
412                                             GrGpuBufferType::kVertex,
413                                             accessPattern);
414                 if (!vb) {
415                     ERRORF(reporter, "Could not create vertex buffer");
416                     return;
417                 }
418 
419                 const void* src = kUnitQuad;
420                 size_t updateSize = sizeof(kUnitQuad);
421                 // The vertices in the VB must be aligned to the size of a vertex (because our draw
422                 // call takes a base vertex index rather than a byte offset). So if we want our
423                 // upload to begin at a non-aligned byte we shift the data in the src buffer so that
424                 // it falls at a vertex alignment in the vb.
425                 std::unique_ptr<char[]> tempSrc;
426                 size_t baseVertex = offset/sizeof(SkPoint);
427                 if (size_t r = offset%sizeof(SkPoint); r != 0) {
428                     size_t pad = sizeof(SkPoint) - r;
429                     updateSize += pad;
430                     if (alignment > 1) {
431                         updateSize = SkAlignTo(updateSize, alignment);
432                     }
433                     ++baseVertex;
434                     tempSrc.reset(new char[updateSize]);
435                     std::memcpy(tempSrc.get() + pad, kUnitQuad, sizeof(kUnitQuad));
436                     src = tempSrc.get();
437                 }
438                 if (piecewise) {
439                     // This is the minimum size we can transfer at once.
440                     size_t pieceSize = alignment;
441 
442                     // Upload each piece from a buffer where the byte before and after the uploaded
443                     // bytes are not the same values as want adjacent to the piece in the buffer.
444                     // Thus, if updateData() transfers extra bytes around the source we should get a
445                     // bad buffer.
446                     auto piece = std::make_unique<unsigned char[]>(pieceSize + 2);
447                     piece[0] = piece[pieceSize + 1] = 0xFF;
448 
449                     for (size_t o = 0; o < updateSize; o += pieceSize) {
450                         memcpy(&piece[1], SkTAddOffset<const void>(src, o), pieceSize);
451                         if (!vb->updateData(&piece[1], offset + o, pieceSize, /*preserve=*/true)) {
452                             ERRORF(reporter, "GrGpuBuffer::updateData returned false.");
453                             return;
454                         }
455                     }
456                 } else if (!vb->updateData(src, offset, updateSize, /*preserve=*/false)) {
457                     ERRORF(reporter, "GrGpuBuffer::updateData returned false.");
458                     return;
459                 }
460 
461                 static constexpr SkColor4f kRed{1, 0, 0, 1};
462 
463                 static constexpr SkRect kBounds{0, 0, 1, 1};
464 
465                 sdc->clear(kRed);
466 
467                 sdc->addDrawOp(nullptr, TestVertexOp::Make(dc,
468                                                            vb,
469                                                            baseVertex,
470                                                            std::size(kUnitQuad),
471                                                            kBounds));
472 
473                 auto color = static_cast<SkPMColor4f*>(pm.addr());
474                 *color = kRed.premul();
475                 if (!sdc->readPixels(dc, pm, {0, 0})) {
476                     ERRORF(reporter, "Read back failed.");
477                     return;
478                 }
479 
480                 static constexpr SkPMColor4f kGreen{0, 1, 0, 1};
481 
482                 REPORTER_ASSERT(reporter, *color == kGreen, "piecewise: %d, offset: %zu",
483                                 piecewise, offset);
484             }
485         }
486     }
487 }
488