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