xref: /aosp_15_r20/external/skia/tests/graphite/ComputeTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2022 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/graphite/Context.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/graphite/Recorder.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/graphite/Recording.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Buffer.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Caps.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ComputePipelineDesc.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ComputeTypes.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ContextPriv.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/RecorderPriv.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ResourceProvider.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/UniformManager.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/compute/ComputeStep.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/compute/DispatchGroup.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/task/ComputeTask.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/task/CopyTask.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/task/SynchronizeToCpuTask.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/task/UploadTask.h"
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker #include "tools/graphite/GraphiteTestContext.h"
30*c8dee2aaSAndroid Build Coastguard Worker 
31*c8dee2aaSAndroid Build Coastguard Worker using namespace skgpu::graphite;
32*c8dee2aaSAndroid Build Coastguard Worker using namespace skiatest::graphite;
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker namespace {
35*c8dee2aaSAndroid Build Coastguard Worker 
map_buffer(Context * context,skiatest::graphite::GraphiteTestContext * testContext,Buffer * buffer,size_t offset)36*c8dee2aaSAndroid Build Coastguard Worker void* map_buffer(Context* context,
37*c8dee2aaSAndroid Build Coastguard Worker                  skiatest::graphite::GraphiteTestContext* testContext,
38*c8dee2aaSAndroid Build Coastguard Worker                  Buffer* buffer,
39*c8dee2aaSAndroid Build Coastguard Worker                  size_t offset) {
40*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(buffer);
41*c8dee2aaSAndroid Build Coastguard Worker     if (context->priv().caps()->bufferMapsAreAsync()) {
42*c8dee2aaSAndroid Build Coastguard Worker         buffer->asyncMap();
43*c8dee2aaSAndroid Build Coastguard Worker         while (!buffer->isMapped()) {
44*c8dee2aaSAndroid Build Coastguard Worker             testContext->tick();
45*c8dee2aaSAndroid Build Coastguard Worker         }
46*c8dee2aaSAndroid Build Coastguard Worker     }
47*c8dee2aaSAndroid Build Coastguard Worker     std::byte* ptr = static_cast<std::byte*>(buffer->map());
48*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(ptr);
49*c8dee2aaSAndroid Build Coastguard Worker 
50*c8dee2aaSAndroid Build Coastguard Worker     return ptr + offset;
51*c8dee2aaSAndroid Build Coastguard Worker }
52*c8dee2aaSAndroid Build Coastguard Worker 
sync_buffer_to_cpu(Recorder * recorder,const Buffer * buffer)53*c8dee2aaSAndroid Build Coastguard Worker sk_sp<Buffer> sync_buffer_to_cpu(Recorder* recorder, const Buffer* buffer) {
54*c8dee2aaSAndroid Build Coastguard Worker     if (recorder->priv().caps()->drawBufferCanBeMappedForReadback()) {
55*c8dee2aaSAndroid Build Coastguard Worker         // `buffer` can be mapped directly, however it may still require a synchronization step
56*c8dee2aaSAndroid Build Coastguard Worker         // by the underlying API (e.g. a managed buffer in Metal). SynchronizeToCpuTask
57*c8dee2aaSAndroid Build Coastguard Worker         // automatically handles this for us.
58*c8dee2aaSAndroid Build Coastguard Worker         recorder->priv().add(SynchronizeToCpuTask::Make(sk_ref_sp(buffer)));
59*c8dee2aaSAndroid Build Coastguard Worker         return sk_ref_sp(buffer);
60*c8dee2aaSAndroid Build Coastguard Worker     }
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker     // The backend requires a transfer buffer for CPU read-back
63*c8dee2aaSAndroid Build Coastguard Worker     auto xferBuffer =
64*c8dee2aaSAndroid Build Coastguard Worker             recorder->priv().resourceProvider()->findOrCreateBuffer(buffer->size(),
65*c8dee2aaSAndroid Build Coastguard Worker                                                                     BufferType::kXferGpuToCpu,
66*c8dee2aaSAndroid Build Coastguard Worker                                                                     AccessPattern::kHostVisible,
67*c8dee2aaSAndroid Build Coastguard Worker                                                                     "ComputeTest_TransferToCpu");
68*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(xferBuffer);
69*c8dee2aaSAndroid Build Coastguard Worker 
70*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(CopyBufferToBufferTask::Make(buffer,
71*c8dee2aaSAndroid Build Coastguard Worker                                                       /*srcOffset=*/0,
72*c8dee2aaSAndroid Build Coastguard Worker                                                       xferBuffer,
73*c8dee2aaSAndroid Build Coastguard Worker                                                       /*dstOffset=*/0,
74*c8dee2aaSAndroid Build Coastguard Worker                                                       buffer->size()));
75*c8dee2aaSAndroid Build Coastguard Worker     return xferBuffer;
76*c8dee2aaSAndroid Build Coastguard Worker }
77*c8dee2aaSAndroid Build Coastguard Worker 
submit_recording(Context * context,GraphiteTestContext * testContext,Recorder * recorder)78*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Recording> submit_recording(Context* context,
79*c8dee2aaSAndroid Build Coastguard Worker                                             GraphiteTestContext* testContext,
80*c8dee2aaSAndroid Build Coastguard Worker                                             Recorder* recorder) {
81*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
82*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
83*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
84*c8dee2aaSAndroid Build Coastguard Worker     }
85*c8dee2aaSAndroid Build Coastguard Worker 
86*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
87*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
88*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
89*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
90*c8dee2aaSAndroid Build Coastguard Worker 
91*c8dee2aaSAndroid Build Coastguard Worker     return recording;
92*c8dee2aaSAndroid Build Coastguard Worker }
93*c8dee2aaSAndroid Build Coastguard Worker 
is_dawn_or_metal_context_type(skiatest::GpuContextType ctxType)94*c8dee2aaSAndroid Build Coastguard Worker bool is_dawn_or_metal_context_type(skiatest::GpuContextType ctxType) {
95*c8dee2aaSAndroid Build Coastguard Worker     return skiatest::IsDawnContextType(ctxType) || skiatest::IsMetalContextType(ctxType);
96*c8dee2aaSAndroid Build Coastguard Worker }
97*c8dee2aaSAndroid Build Coastguard Worker 
98*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
99*c8dee2aaSAndroid Build Coastguard Worker 
100*c8dee2aaSAndroid Build Coastguard Worker #define DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(            \
101*c8dee2aaSAndroid Build Coastguard Worker         name, reporter, graphite_context, test_context)           \
102*c8dee2aaSAndroid Build Coastguard Worker     DEF_GRAPHITE_TEST_FOR_CONTEXTS(name,                          \
103*c8dee2aaSAndroid Build Coastguard Worker                                    is_dawn_or_metal_context_type, \
104*c8dee2aaSAndroid Build Coastguard Worker                                    reporter,                      \
105*c8dee2aaSAndroid Build Coastguard Worker                                    graphite_context,              \
106*c8dee2aaSAndroid Build Coastguard Worker                                    test_context,                  \
107*c8dee2aaSAndroid Build Coastguard Worker                                    CtsEnforcement::kNever)
108*c8dee2aaSAndroid Build Coastguard Worker 
109*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/262427430, b/262429132): Enable this test on other backends once they all support
110*c8dee2aaSAndroid Build Coastguard Worker // compute programs.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_SingleDispatchTest,reporter,context,testContext)111*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_SingleDispatchTest,
112*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
113*c8dee2aaSAndroid Build Coastguard Worker                                               context,
114*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
115*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kProblemSize = 512;
116*c8dee2aaSAndroid Build Coastguard Worker     constexpr float kFactor = 4.f;
117*c8dee2aaSAndroid Build Coastguard Worker 
118*c8dee2aaSAndroid Build Coastguard Worker     // The ComputeStep packs kProblemSize floats into kProblemSize / 4 vectors and each thread
119*c8dee2aaSAndroid Build Coastguard Worker     // processes 1 vector at a time.
120*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = kProblemSize / 4;
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
123*c8dee2aaSAndroid Build Coastguard Worker 
124*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
125*c8dee2aaSAndroid Build Coastguard Worker     public:
126*c8dee2aaSAndroid Build Coastguard Worker         // TODO(skia:40045541): SkSL doesn't support std430 layout well, so the buffers
127*c8dee2aaSAndroid Build Coastguard Worker         // below all pack their data into vectors to be compatible with SPIR-V/WGSL.
128*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
129*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestArrayMultiply",
130*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
131*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
132*c8dee2aaSAndroid Build Coastguard Worker                     // Input buffer:
133*c8dee2aaSAndroid Build Coastguard Worker                     {
134*c8dee2aaSAndroid Build Coastguard Worker                         // TODO(b/299979165): Declare this binding as read-only.
135*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
136*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kPrivate,
137*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
138*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"inputBlock {\n"
139*c8dee2aaSAndroid Build Coastguard Worker                             "    float factor;\n"
140*c8dee2aaSAndroid Build Coastguard Worker                             "    layout(offset=16) float4 in_data[];\n"
141*c8dee2aaSAndroid Build Coastguard Worker                             "}",
142*c8dee2aaSAndroid Build Coastguard Worker                     },
143*c8dee2aaSAndroid Build Coastguard Worker                     // Output buffer:
144*c8dee2aaSAndroid Build Coastguard Worker                     {
145*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
146*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,  // shared to allow us to access it from the
147*c8dee2aaSAndroid Build Coastguard Worker                                                      // Builder
148*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,  // mappable for read-back
149*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
150*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock { float4 out_data[]; }",
151*c8dee2aaSAndroid Build Coastguard Worker                     }
152*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
153*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
154*c8dee2aaSAndroid Build Coastguard Worker 
155*c8dee2aaSAndroid Build Coastguard Worker         // A kernel that multiplies a large array of floats by a supplied factor.
156*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
157*c8dee2aaSAndroid Build Coastguard Worker             return R"(
158*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
159*c8dee2aaSAndroid Build Coastguard Worker                     out_data[sk_GlobalInvocationID.x] = in_data[sk_GlobalInvocationID.x] * factor;
160*c8dee2aaSAndroid Build Coastguard Worker                 }
161*c8dee2aaSAndroid Build Coastguard Worker             )";
162*c8dee2aaSAndroid Build Coastguard Worker         }
163*c8dee2aaSAndroid Build Coastguard Worker 
164*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
165*c8dee2aaSAndroid Build Coastguard Worker             if (index == 0) {
166*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(r.fFlow == DataFlow::kPrivate);
167*c8dee2aaSAndroid Build Coastguard Worker                 return sizeof(float) * (kProblemSize + 4);
168*c8dee2aaSAndroid Build Coastguard Worker             }
169*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 1);
170*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 0);
171*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
172*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(float) * kProblemSize;
173*c8dee2aaSAndroid Build Coastguard Worker         }
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
176*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
177*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
178*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
179*c8dee2aaSAndroid Build Coastguard Worker             // Only initialize the input buffer.
180*c8dee2aaSAndroid Build Coastguard Worker             if (resourceIndex != 0) {
181*c8dee2aaSAndroid Build Coastguard Worker                 return;
182*c8dee2aaSAndroid Build Coastguard Worker             }
183*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kPrivate);
184*c8dee2aaSAndroid Build Coastguard Worker 
185*c8dee2aaSAndroid Build Coastguard Worker             size_t dataCount = sizeof(float) * (kProblemSize + 4);
186*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(bufferSize == dataCount);
187*c8dee2aaSAndroid Build Coastguard Worker             SkSpan<float> inData(static_cast<float*>(buffer), dataCount);
188*c8dee2aaSAndroid Build Coastguard Worker             inData[0] = kFactor;
189*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned int i = 0; i < kProblemSize; ++i) {
190*c8dee2aaSAndroid Build Coastguard Worker                 inData[i + 4] = i + 1;
191*c8dee2aaSAndroid Build Coastguard Worker             }
192*c8dee2aaSAndroid Build Coastguard Worker         }
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
195*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
196*c8dee2aaSAndroid Build Coastguard Worker         }
197*c8dee2aaSAndroid Build Coastguard Worker     } step;
198*c8dee2aaSAndroid Build Coastguard Worker 
199*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
200*c8dee2aaSAndroid Build Coastguard Worker     if (!builder.appendStep(&step)) {
201*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to add ComputeStep to DispatchGroup");
202*c8dee2aaSAndroid Build Coastguard Worker         return;
203*c8dee2aaSAndroid Build Coastguard Worker     }
204*c8dee2aaSAndroid Build Coastguard Worker 
205*c8dee2aaSAndroid Build Coastguard Worker     // The output buffer should have been placed in the right output slot.
206*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo outputInfo = builder.getSharedBufferResource(0);
207*c8dee2aaSAndroid Build Coastguard Worker     if (!outputInfo) {
208*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to allocate an output buffer at slot 0");
209*c8dee2aaSAndroid Build Coastguard Worker         return;
210*c8dee2aaSAndroid Build Coastguard Worker     }
211*c8dee2aaSAndroid Build Coastguard Worker 
212*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
213*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
214*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
215*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
216*c8dee2aaSAndroid Build Coastguard Worker 
217*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
218*c8dee2aaSAndroid Build Coastguard Worker     auto outputBuffer = sync_buffer_to_cpu(recorder.get(), outputInfo.fBuffer);
219*c8dee2aaSAndroid Build Coastguard Worker 
220*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
221*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
222*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
223*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
224*c8dee2aaSAndroid Build Coastguard Worker         return;
225*c8dee2aaSAndroid Build Coastguard Worker     }
226*c8dee2aaSAndroid Build Coastguard Worker 
227*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
228*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
229*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
230*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
231*c8dee2aaSAndroid Build Coastguard Worker 
232*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
233*c8dee2aaSAndroid Build Coastguard Worker     float* outData = static_cast<float*>(
234*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, outputBuffer.get(), outputInfo.fOffset));
235*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(outputBuffer->isMapped() && outData != nullptr);
236*c8dee2aaSAndroid Build Coastguard Worker     for (unsigned int i = 0; i < kProblemSize; ++i) {
237*c8dee2aaSAndroid Build Coastguard Worker         const float expected = (i + 1) * kFactor;
238*c8dee2aaSAndroid Build Coastguard Worker         const float found = outData[i];
239*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, expected == found, "expected '%f', found '%f'", expected, found);
240*c8dee2aaSAndroid Build Coastguard Worker     }
241*c8dee2aaSAndroid Build Coastguard Worker }
242*c8dee2aaSAndroid Build Coastguard Worker 
243*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/262427430, b/262429132): Enable this test on other backends once they all support
244*c8dee2aaSAndroid Build Coastguard Worker // compute programs.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_DispatchGroupTest,reporter,context,testContext)245*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_DispatchGroupTest,
246*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
247*c8dee2aaSAndroid Build Coastguard Worker                                               context,
248*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
249*c8dee2aaSAndroid Build Coastguard Worker     // TODO(b/315834710): This fails on Dawn D3D11
250*c8dee2aaSAndroid Build Coastguard Worker     if (testContext->contextType() == skgpu::ContextType::kDawn_D3D11) {
251*c8dee2aaSAndroid Build Coastguard Worker         return;
252*c8dee2aaSAndroid Build Coastguard Worker     }
253*c8dee2aaSAndroid Build Coastguard Worker 
254*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kProblemSize = 512;
255*c8dee2aaSAndroid Build Coastguard Worker     constexpr float kFactor1 = 4.f;
256*c8dee2aaSAndroid Build Coastguard Worker     constexpr float kFactor2 = 3.f;
257*c8dee2aaSAndroid Build Coastguard Worker 
258*c8dee2aaSAndroid Build Coastguard Worker     // The ComputeStep packs kProblemSize floats into kProblemSize / 4 vectors and each thread
259*c8dee2aaSAndroid Build Coastguard Worker     // processes 1 vector at a time.
260*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = kProblemSize / 4;
261*c8dee2aaSAndroid Build Coastguard Worker 
262*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
263*c8dee2aaSAndroid Build Coastguard Worker 
264*c8dee2aaSAndroid Build Coastguard Worker     // Define two steps that perform two multiplication passes over the same input.
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep1 : public ComputeStep {
267*c8dee2aaSAndroid Build Coastguard Worker     public:
268*c8dee2aaSAndroid Build Coastguard Worker         // TODO(skia:40045541): SkSL doesn't support std430 layout well, so the buffers
269*c8dee2aaSAndroid Build Coastguard Worker         // below all pack their data into vectors to be compatible with SPIR-V/WGSL.
270*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep1() : ComputeStep(
271*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestArrayMultiplyFirstPass",
272*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
273*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
274*c8dee2aaSAndroid Build Coastguard Worker                     // Input buffer:
275*c8dee2aaSAndroid Build Coastguard Worker                     {
276*c8dee2aaSAndroid Build Coastguard Worker                         // TODO(b/299979165): Declare this binding as read-only.
277*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
278*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kPrivate,
279*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,  // mappable for read-back
280*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"inputBlock {\n"
281*c8dee2aaSAndroid Build Coastguard Worker                             "    float factor;\n"
282*c8dee2aaSAndroid Build Coastguard Worker                             "    layout(offset=16) float4 in_data[];\n"
283*c8dee2aaSAndroid Build Coastguard Worker                             "}",
284*c8dee2aaSAndroid Build Coastguard Worker                     },
285*c8dee2aaSAndroid Build Coastguard Worker                     // Output buffers:
286*c8dee2aaSAndroid Build Coastguard Worker                     {
287*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
288*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
289*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,  // GPU-only, read by second step
290*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
291*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock1 { float4 forward_data[]; }",
292*c8dee2aaSAndroid Build Coastguard Worker                     },
293*c8dee2aaSAndroid Build Coastguard Worker                     {
294*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
295*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
296*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,  // mappable for read-back
297*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/1,
298*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock2 { float2 extra_data; }",
299*c8dee2aaSAndroid Build Coastguard Worker                     }
300*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
301*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep1() override = default;
302*c8dee2aaSAndroid Build Coastguard Worker 
303*c8dee2aaSAndroid Build Coastguard Worker         // A kernel that multiplies a large array of floats by a supplied factor.
304*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
305*c8dee2aaSAndroid Build Coastguard Worker             return R"(
306*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
307*c8dee2aaSAndroid Build Coastguard Worker                     uint idx = sk_GlobalInvocationID.x;
308*c8dee2aaSAndroid Build Coastguard Worker                     forward_data[idx] = in_data[idx] * factor;
309*c8dee2aaSAndroid Build Coastguard Worker                     if (idx == 0) {
310*c8dee2aaSAndroid Build Coastguard Worker                         extra_data.x = factor;
311*c8dee2aaSAndroid Build Coastguard Worker                         extra_data.y = 2 * factor;
312*c8dee2aaSAndroid Build Coastguard Worker                     }
313*c8dee2aaSAndroid Build Coastguard Worker                 }
314*c8dee2aaSAndroid Build Coastguard Worker             )";
315*c8dee2aaSAndroid Build Coastguard Worker         }
316*c8dee2aaSAndroid Build Coastguard Worker 
317*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
318*c8dee2aaSAndroid Build Coastguard Worker             if (index == 0) {
319*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(r.fFlow == DataFlow::kPrivate);
320*c8dee2aaSAndroid Build Coastguard Worker                 return sizeof(float) * (kProblemSize + 4);
321*c8dee2aaSAndroid Build Coastguard Worker             }
322*c8dee2aaSAndroid Build Coastguard Worker             if (index == 1) {
323*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(r.fFlow == DataFlow::kShared);
324*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(r.fSlot == 0);
325*c8dee2aaSAndroid Build Coastguard Worker                 return sizeof(float) * kProblemSize;
326*c8dee2aaSAndroid Build Coastguard Worker             }
327*c8dee2aaSAndroid Build Coastguard Worker 
328*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 2);
329*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 1);
330*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
331*c8dee2aaSAndroid Build Coastguard Worker             return 2 * sizeof(float);
332*c8dee2aaSAndroid Build Coastguard Worker         }
333*c8dee2aaSAndroid Build Coastguard Worker 
334*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
335*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
336*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
337*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
338*c8dee2aaSAndroid Build Coastguard Worker             if (resourceIndex != 0) {
339*c8dee2aaSAndroid Build Coastguard Worker                 return;
340*c8dee2aaSAndroid Build Coastguard Worker             }
341*c8dee2aaSAndroid Build Coastguard Worker 
342*c8dee2aaSAndroid Build Coastguard Worker             size_t dataCount = sizeof(float) * (kProblemSize + 4);
343*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(bufferSize == dataCount);
344*c8dee2aaSAndroid Build Coastguard Worker             SkSpan<float> inData(static_cast<float*>(buffer), dataCount);
345*c8dee2aaSAndroid Build Coastguard Worker             inData[0] = kFactor1;
346*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned int i = 0; i < kProblemSize; ++i) {
347*c8dee2aaSAndroid Build Coastguard Worker                 inData[i + 4] = i + 1;
348*c8dee2aaSAndroid Build Coastguard Worker             }
349*c8dee2aaSAndroid Build Coastguard Worker         }
350*c8dee2aaSAndroid Build Coastguard Worker 
351*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
352*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
353*c8dee2aaSAndroid Build Coastguard Worker         }
354*c8dee2aaSAndroid Build Coastguard Worker     } step1;
355*c8dee2aaSAndroid Build Coastguard Worker 
356*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep2 : public ComputeStep {
357*c8dee2aaSAndroid Build Coastguard Worker     public:
358*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep2() : ComputeStep(
359*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestArrayMultiplySecondPass",
360*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
361*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
362*c8dee2aaSAndroid Build Coastguard Worker                     // Input buffer:
363*c8dee2aaSAndroid Build Coastguard Worker                     {
364*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
365*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
366*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,  // GPU-only
367*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0, // this is the output from the first step
368*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"inputBlock { float4 in_data[]; }",
369*c8dee2aaSAndroid Build Coastguard Worker                     },
370*c8dee2aaSAndroid Build Coastguard Worker                     {
371*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
372*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kPrivate,
373*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
374*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"factorBlock { float factor; }"
375*c8dee2aaSAndroid Build Coastguard Worker                     },
376*c8dee2aaSAndroid Build Coastguard Worker                     // Output buffer:
377*c8dee2aaSAndroid Build Coastguard Worker                     {
378*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
379*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
380*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,  // mappable for read-back
381*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/2,
382*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock { float4 out_data[]; }",
383*c8dee2aaSAndroid Build Coastguard Worker                     }
384*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
385*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep2() override = default;
386*c8dee2aaSAndroid Build Coastguard Worker 
387*c8dee2aaSAndroid Build Coastguard Worker         // A kernel that multiplies a large array of floats by a supplied factor.
388*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
389*c8dee2aaSAndroid Build Coastguard Worker             return R"(
390*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
391*c8dee2aaSAndroid Build Coastguard Worker                     out_data[sk_GlobalInvocationID.x] = in_data[sk_GlobalInvocationID.x] * factor;
392*c8dee2aaSAndroid Build Coastguard Worker                 }
393*c8dee2aaSAndroid Build Coastguard Worker             )";
394*c8dee2aaSAndroid Build Coastguard Worker         }
395*c8dee2aaSAndroid Build Coastguard Worker 
396*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
397*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index != 0);
398*c8dee2aaSAndroid Build Coastguard Worker             if (index == 1) {
399*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(r.fFlow == DataFlow::kPrivate);
400*c8dee2aaSAndroid Build Coastguard Worker                 return sizeof(float) * 4;
401*c8dee2aaSAndroid Build Coastguard Worker             }
402*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 2);
403*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 2);
404*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
405*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(float) * kProblemSize;
406*c8dee2aaSAndroid Build Coastguard Worker         }
407*c8dee2aaSAndroid Build Coastguard Worker 
408*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
409*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
410*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
411*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
412*c8dee2aaSAndroid Build Coastguard Worker             if (resourceIndex != 1) {
413*c8dee2aaSAndroid Build Coastguard Worker                 return;
414*c8dee2aaSAndroid Build Coastguard Worker             }
415*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kPrivate);
416*c8dee2aaSAndroid Build Coastguard Worker             *static_cast<float*>(buffer) = kFactor2;
417*c8dee2aaSAndroid Build Coastguard Worker         }
418*c8dee2aaSAndroid Build Coastguard Worker 
419*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
420*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
421*c8dee2aaSAndroid Build Coastguard Worker         }
422*c8dee2aaSAndroid Build Coastguard Worker     } step2;
423*c8dee2aaSAndroid Build Coastguard Worker 
424*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
425*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step1);
426*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step2);
427*c8dee2aaSAndroid Build Coastguard Worker 
428*c8dee2aaSAndroid Build Coastguard Worker     // Slots 0, 1, and 2 should all contain shared buffers. Slot 1 contains the extra output buffer
429*c8dee2aaSAndroid Build Coastguard Worker     // from step 1 while slot 2 contains the result of the second multiplication pass from step 1.
430*c8dee2aaSAndroid Build Coastguard Worker     // Slot 0 is not mappable.
431*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
432*c8dee2aaSAndroid Build Coastguard Worker                     std::holds_alternative<BindBufferInfo>(builder.outputTable().fSharedSlots[0]),
433*c8dee2aaSAndroid Build Coastguard Worker                     "shared resource at slot 0 is missing");
434*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo outputInfo = builder.getSharedBufferResource(2);
435*c8dee2aaSAndroid Build Coastguard Worker     if (!outputInfo) {
436*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to allocate an output buffer at slot 0");
437*c8dee2aaSAndroid Build Coastguard Worker         return;
438*c8dee2aaSAndroid Build Coastguard Worker     }
439*c8dee2aaSAndroid Build Coastguard Worker 
440*c8dee2aaSAndroid Build Coastguard Worker     // Extra output buffer from step 1 (corresponding to 'outputBlock2')
441*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo extraOutputInfo = builder.getSharedBufferResource(1);
442*c8dee2aaSAndroid Build Coastguard Worker     if (!extraOutputInfo) {
443*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 1 is missing");
444*c8dee2aaSAndroid Build Coastguard Worker         return;
445*c8dee2aaSAndroid Build Coastguard Worker     }
446*c8dee2aaSAndroid Build Coastguard Worker 
447*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
448*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
449*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
450*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
451*c8dee2aaSAndroid Build Coastguard Worker 
452*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffers get synchronized to the CPU once the GPU submission has finished.
453*c8dee2aaSAndroid Build Coastguard Worker     auto outputBuffer = sync_buffer_to_cpu(recorder.get(), outputInfo.fBuffer);
454*c8dee2aaSAndroid Build Coastguard Worker     auto extraOutputBuffer = sync_buffer_to_cpu(recorder.get(), extraOutputInfo.fBuffer);
455*c8dee2aaSAndroid Build Coastguard Worker 
456*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
457*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
458*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
459*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
460*c8dee2aaSAndroid Build Coastguard Worker         return;
461*c8dee2aaSAndroid Build Coastguard Worker     }
462*c8dee2aaSAndroid Build Coastguard Worker 
463*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
464*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
465*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
466*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
467*c8dee2aaSAndroid Build Coastguard Worker 
468*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer from step 2
469*c8dee2aaSAndroid Build Coastguard Worker     float* outData = static_cast<float*>(
470*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, outputBuffer.get(), outputInfo.fOffset));
471*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(outputBuffer->isMapped() && outData != nullptr);
472*c8dee2aaSAndroid Build Coastguard Worker     for (unsigned int i = 0; i < kProblemSize; ++i) {
473*c8dee2aaSAndroid Build Coastguard Worker         const float expected = (i + 1) * kFactor1 * kFactor2;
474*c8dee2aaSAndroid Build Coastguard Worker         const float found = outData[i];
475*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, expected == found, "expected '%f', found '%f'", expected, found);
476*c8dee2aaSAndroid Build Coastguard Worker     }
477*c8dee2aaSAndroid Build Coastguard Worker 
478*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the extra output buffer from step 1
479*c8dee2aaSAndroid Build Coastguard Worker     float* extraOutData = static_cast<float*>(
480*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, extraOutputBuffer.get(), extraOutputInfo.fOffset));
481*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(extraOutputBuffer->isMapped() && extraOutData != nullptr);
482*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
483*c8dee2aaSAndroid Build Coastguard Worker                     kFactor1 == extraOutData[0],
484*c8dee2aaSAndroid Build Coastguard Worker                     "expected '%f', found '%f'",
485*c8dee2aaSAndroid Build Coastguard Worker                     kFactor1,
486*c8dee2aaSAndroid Build Coastguard Worker                     extraOutData[0]);
487*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
488*c8dee2aaSAndroid Build Coastguard Worker                     2 * kFactor1 == extraOutData[1],
489*c8dee2aaSAndroid Build Coastguard Worker                     "expected '%f', found '%f'",
490*c8dee2aaSAndroid Build Coastguard Worker                     2 * kFactor2,
491*c8dee2aaSAndroid Build Coastguard Worker                     extraOutData[1]);
492*c8dee2aaSAndroid Build Coastguard Worker }
493*c8dee2aaSAndroid Build Coastguard Worker 
494*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/262427430, b/262429132): Enable this test on other backends once they all support
495*c8dee2aaSAndroid Build Coastguard Worker // compute programs.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_UniformBufferTest,reporter,context,testContext)496*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_UniformBufferTest,
497*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
498*c8dee2aaSAndroid Build Coastguard Worker                                               context,
499*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
500*c8dee2aaSAndroid Build Coastguard Worker     // TODO(b/315834710): This fails on Dawn D3D11
501*c8dee2aaSAndroid Build Coastguard Worker     if (testContext->contextType() == skgpu::ContextType::kDawn_D3D11) {
502*c8dee2aaSAndroid Build Coastguard Worker         return;
503*c8dee2aaSAndroid Build Coastguard Worker     }
504*c8dee2aaSAndroid Build Coastguard Worker 
505*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kProblemSize = 512;
506*c8dee2aaSAndroid Build Coastguard Worker     constexpr float kFactor = 4.f;
507*c8dee2aaSAndroid Build Coastguard Worker 
508*c8dee2aaSAndroid Build Coastguard Worker     // The ComputeStep packs kProblemSize floats into kProblemSize / 4 vectors and each thread
509*c8dee2aaSAndroid Build Coastguard Worker     // processes 1 vector at a time.
510*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = kProblemSize / 4;
511*c8dee2aaSAndroid Build Coastguard Worker 
512*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
513*c8dee2aaSAndroid Build Coastguard Worker 
514*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
515*c8dee2aaSAndroid Build Coastguard Worker     public:
516*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
517*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestArrayMultiply",
518*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
519*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
520*c8dee2aaSAndroid Build Coastguard Worker                     // Uniform buffer:
521*c8dee2aaSAndroid Build Coastguard Worker                     {
522*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kUniformBuffer,
523*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kPrivate,
524*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
525*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"uniformBlock { float factor; }"
526*c8dee2aaSAndroid Build Coastguard Worker                     },
527*c8dee2aaSAndroid Build Coastguard Worker                     // Input buffer:
528*c8dee2aaSAndroid Build Coastguard Worker                     {
529*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
530*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kPrivate,
531*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
532*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"inputBlock { float4 in_data[]; }",
533*c8dee2aaSAndroid Build Coastguard Worker                     },
534*c8dee2aaSAndroid Build Coastguard Worker                     // Output buffer:
535*c8dee2aaSAndroid Build Coastguard Worker                     {
536*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
537*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,  // shared to allow us to access it from the
538*c8dee2aaSAndroid Build Coastguard Worker                                                      // Builder
539*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,  // mappable for read-back
540*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
541*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock { float4 out_data[]; }",
542*c8dee2aaSAndroid Build Coastguard Worker                     }
543*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
544*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
545*c8dee2aaSAndroid Build Coastguard Worker 
546*c8dee2aaSAndroid Build Coastguard Worker         // A kernel that multiplies a large array of floats by a supplied factor.
547*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
548*c8dee2aaSAndroid Build Coastguard Worker             return R"(
549*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
550*c8dee2aaSAndroid Build Coastguard Worker                     out_data[sk_GlobalInvocationID.x] = in_data[sk_GlobalInvocationID.x] * factor;
551*c8dee2aaSAndroid Build Coastguard Worker                 }
552*c8dee2aaSAndroid Build Coastguard Worker             )";
553*c8dee2aaSAndroid Build Coastguard Worker         }
554*c8dee2aaSAndroid Build Coastguard Worker 
555*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
556*c8dee2aaSAndroid Build Coastguard Worker             if (index == 0) {
557*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(r.fFlow == DataFlow::kPrivate);
558*c8dee2aaSAndroid Build Coastguard Worker                 return sizeof(float);
559*c8dee2aaSAndroid Build Coastguard Worker             }
560*c8dee2aaSAndroid Build Coastguard Worker             if (index == 1) {
561*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(r.fFlow == DataFlow::kPrivate);
562*c8dee2aaSAndroid Build Coastguard Worker                 return sizeof(float) * kProblemSize;
563*c8dee2aaSAndroid Build Coastguard Worker             }
564*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 2);
565*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 0);
566*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
567*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(float) * kProblemSize;
568*c8dee2aaSAndroid Build Coastguard Worker         }
569*c8dee2aaSAndroid Build Coastguard Worker 
570*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
571*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
572*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
573*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
574*c8dee2aaSAndroid Build Coastguard Worker             // Only initialize the input storage buffer.
575*c8dee2aaSAndroid Build Coastguard Worker             if (resourceIndex != 1) {
576*c8dee2aaSAndroid Build Coastguard Worker                 return;
577*c8dee2aaSAndroid Build Coastguard Worker             }
578*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kPrivate);
579*c8dee2aaSAndroid Build Coastguard Worker             size_t dataCount = sizeof(float) * kProblemSize;
580*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(bufferSize == dataCount);
581*c8dee2aaSAndroid Build Coastguard Worker             SkSpan<float> inData(static_cast<float*>(buffer), dataCount);
582*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned int i = 0; i < kProblemSize; ++i) {
583*c8dee2aaSAndroid Build Coastguard Worker                 inData[i] = i + 1;
584*c8dee2aaSAndroid Build Coastguard Worker             }
585*c8dee2aaSAndroid Build Coastguard Worker         }
586*c8dee2aaSAndroid Build Coastguard Worker 
587*c8dee2aaSAndroid Build Coastguard Worker         void prepareUniformBuffer(int resourceIndex,
588*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc&,
589*c8dee2aaSAndroid Build Coastguard Worker                                   UniformManager* mgr) const override {
590*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 0);
591*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGCODE(
592*c8dee2aaSAndroid Build Coastguard Worker                 const Uniform uniforms[] = {{"factor", SkSLType::kFloat}};
593*c8dee2aaSAndroid Build Coastguard Worker                 mgr->setExpectedUniforms(uniforms, /*isSubstruct=*/false);
594*c8dee2aaSAndroid Build Coastguard Worker             )
595*c8dee2aaSAndroid Build Coastguard Worker             mgr->write(kFactor);
596*c8dee2aaSAndroid Build Coastguard Worker         }
597*c8dee2aaSAndroid Build Coastguard Worker 
598*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
599*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
600*c8dee2aaSAndroid Build Coastguard Worker         }
601*c8dee2aaSAndroid Build Coastguard Worker     } step;
602*c8dee2aaSAndroid Build Coastguard Worker 
603*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
604*c8dee2aaSAndroid Build Coastguard Worker     if (!builder.appendStep(&step)) {
605*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to add ComputeStep to DispatchGroup");
606*c8dee2aaSAndroid Build Coastguard Worker         return;
607*c8dee2aaSAndroid Build Coastguard Worker     }
608*c8dee2aaSAndroid Build Coastguard Worker 
609*c8dee2aaSAndroid Build Coastguard Worker     // The output buffer should have been placed in the right output slot.
610*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo outputInfo = builder.getSharedBufferResource(0);
611*c8dee2aaSAndroid Build Coastguard Worker     if (!outputInfo) {
612*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to allocate an output buffer at slot 0");
613*c8dee2aaSAndroid Build Coastguard Worker         return;
614*c8dee2aaSAndroid Build Coastguard Worker     }
615*c8dee2aaSAndroid Build Coastguard Worker 
616*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
617*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
618*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
619*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
620*c8dee2aaSAndroid Build Coastguard Worker 
621*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
622*c8dee2aaSAndroid Build Coastguard Worker     auto outputBuffer = sync_buffer_to_cpu(recorder.get(), outputInfo.fBuffer);
623*c8dee2aaSAndroid Build Coastguard Worker 
624*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
625*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
626*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
627*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
628*c8dee2aaSAndroid Build Coastguard Worker         return;
629*c8dee2aaSAndroid Build Coastguard Worker     }
630*c8dee2aaSAndroid Build Coastguard Worker 
631*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
632*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
633*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
634*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
635*c8dee2aaSAndroid Build Coastguard Worker 
636*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
637*c8dee2aaSAndroid Build Coastguard Worker     float* outData = static_cast<float*>(
638*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, outputBuffer.get(), outputInfo.fOffset));
639*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(outputBuffer->isMapped() && outData != nullptr);
640*c8dee2aaSAndroid Build Coastguard Worker     for (unsigned int i = 0; i < kProblemSize; ++i) {
641*c8dee2aaSAndroid Build Coastguard Worker         const float expected = (i + 1) * kFactor;
642*c8dee2aaSAndroid Build Coastguard Worker         const float found = outData[i];
643*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, expected == found, "expected '%f', found '%f'", expected, found);
644*c8dee2aaSAndroid Build Coastguard Worker     }
645*c8dee2aaSAndroid Build Coastguard Worker }
646*c8dee2aaSAndroid Build Coastguard Worker 
647*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/262427430, b/262429132): Enable this test on other backends once they all support
648*c8dee2aaSAndroid Build Coastguard Worker // compute programs.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ExternallyAssignedBuffer,reporter,context,testContext)649*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ExternallyAssignedBuffer,
650*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
651*c8dee2aaSAndroid Build Coastguard Worker                                               context,
652*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
653*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kProblemSize = 512;
654*c8dee2aaSAndroid Build Coastguard Worker     constexpr float kFactor = 4.f;
655*c8dee2aaSAndroid Build Coastguard Worker 
656*c8dee2aaSAndroid Build Coastguard Worker     // The ComputeStep packs kProblemSize floats into kProblemSize / 4 vectors and each thread
657*c8dee2aaSAndroid Build Coastguard Worker     // processes 1 vector at a time.
658*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = kProblemSize / 4;
659*c8dee2aaSAndroid Build Coastguard Worker 
660*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
661*c8dee2aaSAndroid Build Coastguard Worker 
662*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
663*c8dee2aaSAndroid Build Coastguard Worker     public:
664*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
665*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"ExternallyAssignedBuffer",
666*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
667*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
668*c8dee2aaSAndroid Build Coastguard Worker                     // Input buffer:
669*c8dee2aaSAndroid Build Coastguard Worker                     {
670*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
671*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kPrivate,
672*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
673*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"inputBlock {\n"
674*c8dee2aaSAndroid Build Coastguard Worker                                  "    float factor;\n"
675*c8dee2aaSAndroid Build Coastguard Worker                                  "    layout(offset = 16) float4 in_data[];\n"
676*c8dee2aaSAndroid Build Coastguard Worker                                  "}\n",
677*c8dee2aaSAndroid Build Coastguard Worker                     },
678*c8dee2aaSAndroid Build Coastguard Worker                     // Output buffer:
679*c8dee2aaSAndroid Build Coastguard Worker                     {
680*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
681*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,  // shared to allow us to access it from the
682*c8dee2aaSAndroid Build Coastguard Worker                                                      // Builder
683*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,  // mappable for read-back
684*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
685*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock { float4 out_data[]; }",
686*c8dee2aaSAndroid Build Coastguard Worker                     }
687*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
688*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
689*c8dee2aaSAndroid Build Coastguard Worker 
690*c8dee2aaSAndroid Build Coastguard Worker         // A kernel that multiplies a large array of floats by a supplied factor.
691*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
692*c8dee2aaSAndroid Build Coastguard Worker             return R"(
693*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
694*c8dee2aaSAndroid Build Coastguard Worker                     out_data[sk_GlobalInvocationID.x] = in_data[sk_GlobalInvocationID.x] * factor;
695*c8dee2aaSAndroid Build Coastguard Worker                 }
696*c8dee2aaSAndroid Build Coastguard Worker             )";
697*c8dee2aaSAndroid Build Coastguard Worker         }
698*c8dee2aaSAndroid Build Coastguard Worker 
699*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int resourceIndex, const ResourceDesc& r) const override {
700*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 0);
701*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kPrivate);
702*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(float) * (kProblemSize + 4);
703*c8dee2aaSAndroid Build Coastguard Worker         }
704*c8dee2aaSAndroid Build Coastguard Worker 
705*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
706*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
707*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
708*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
709*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 0);
710*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kPrivate);
711*c8dee2aaSAndroid Build Coastguard Worker 
712*c8dee2aaSAndroid Build Coastguard Worker             size_t dataCount = sizeof(float) * (kProblemSize + 4);
713*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(bufferSize == dataCount);
714*c8dee2aaSAndroid Build Coastguard Worker             SkSpan<float> inData(static_cast<float*>(buffer), dataCount);
715*c8dee2aaSAndroid Build Coastguard Worker             inData[0] = kFactor;
716*c8dee2aaSAndroid Build Coastguard Worker             for (unsigned int i = 0; i < kProblemSize; ++i) {
717*c8dee2aaSAndroid Build Coastguard Worker                 inData[i + 4] = i + 1;
718*c8dee2aaSAndroid Build Coastguard Worker             }
719*c8dee2aaSAndroid Build Coastguard Worker         }
720*c8dee2aaSAndroid Build Coastguard Worker     } step;
721*c8dee2aaSAndroid Build Coastguard Worker 
722*c8dee2aaSAndroid Build Coastguard Worker     // We allocate a buffer and directly assign it to the DispatchGroup::Builder. The ComputeStep
723*c8dee2aaSAndroid Build Coastguard Worker     // will not participate in the creation of this buffer.
724*c8dee2aaSAndroid Build Coastguard Worker     auto [_, outputInfo] =
725*c8dee2aaSAndroid Build Coastguard Worker             recorder->priv().drawBufferManager()->getStoragePointer(sizeof(float) * kProblemSize);
726*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, outputInfo, "Failed to allocate output buffer");
727*c8dee2aaSAndroid Build Coastguard Worker 
728*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
729*c8dee2aaSAndroid Build Coastguard Worker     builder.assignSharedBuffer(outputInfo, 0);
730*c8dee2aaSAndroid Build Coastguard Worker 
731*c8dee2aaSAndroid Build Coastguard Worker     // Initialize the step with a pre-determined global size
732*c8dee2aaSAndroid Build Coastguard Worker     if (!builder.appendStep(&step, {WorkgroupSize(1, 1, 1)})) {
733*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to add ComputeStep to DispatchGroup");
734*c8dee2aaSAndroid Build Coastguard Worker         return;
735*c8dee2aaSAndroid Build Coastguard Worker     }
736*c8dee2aaSAndroid Build Coastguard Worker 
737*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
738*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
739*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
740*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
741*c8dee2aaSAndroid Build Coastguard Worker 
742*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
743*c8dee2aaSAndroid Build Coastguard Worker     auto outputBuffer = sync_buffer_to_cpu(recorder.get(), outputInfo.fBuffer);
744*c8dee2aaSAndroid Build Coastguard Worker 
745*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
746*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
747*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
748*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
749*c8dee2aaSAndroid Build Coastguard Worker         return;
750*c8dee2aaSAndroid Build Coastguard Worker     }
751*c8dee2aaSAndroid Build Coastguard Worker 
752*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
753*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
754*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
755*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
756*c8dee2aaSAndroid Build Coastguard Worker 
757*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
758*c8dee2aaSAndroid Build Coastguard Worker     float* outData = static_cast<float*>(
759*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, outputBuffer.get(), outputInfo.fOffset));
760*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(outputBuffer->isMapped() && outData != nullptr);
761*c8dee2aaSAndroid Build Coastguard Worker     for (unsigned int i = 0; i < kProblemSize; ++i) {
762*c8dee2aaSAndroid Build Coastguard Worker         const float expected = (i + 1) * kFactor;
763*c8dee2aaSAndroid Build Coastguard Worker         const float found = outData[i];
764*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, expected == found, "expected '%f', found '%f'", expected, found);
765*c8dee2aaSAndroid Build Coastguard Worker     }
766*c8dee2aaSAndroid Build Coastguard Worker }
767*c8dee2aaSAndroid Build Coastguard Worker 
768*c8dee2aaSAndroid Build Coastguard Worker // Tests the storage texture binding for a compute dispatch that writes the same color to every
769*c8dee2aaSAndroid Build Coastguard Worker // pixel of a storage texture.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_StorageTexture,reporter,context,testContext)770*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_StorageTexture,
771*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
772*c8dee2aaSAndroid Build Coastguard Worker                                               context,
773*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
774*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
775*c8dee2aaSAndroid Build Coastguard Worker 
776*c8dee2aaSAndroid Build Coastguard Worker     // For this test we allocate a 8x8 tile which is written to by a single workgroup of the same
777*c8dee2aaSAndroid Build Coastguard Worker     // size.
778*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kDim = 8;
779*c8dee2aaSAndroid Build Coastguard Worker 
780*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
781*c8dee2aaSAndroid Build Coastguard Worker     public:
782*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
783*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestStorageTexture",
784*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kDim, kDim, 1},
785*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
786*c8dee2aaSAndroid Build Coastguard Worker                     {
787*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kWriteOnlyStorageTexture,
788*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
789*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
790*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
791*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"dst",
792*c8dee2aaSAndroid Build Coastguard Worker                     }
793*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
794*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
795*c8dee2aaSAndroid Build Coastguard Worker 
796*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
797*c8dee2aaSAndroid Build Coastguard Worker             return R"(
798*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
799*c8dee2aaSAndroid Build Coastguard Worker                     textureWrite(dst, sk_LocalInvocationID.xy, half4(0.0, 1.0, 0.0, 1.0));
800*c8dee2aaSAndroid Build Coastguard Worker                 }
801*c8dee2aaSAndroid Build Coastguard Worker             )";
802*c8dee2aaSAndroid Build Coastguard Worker         }
803*c8dee2aaSAndroid Build Coastguard Worker 
804*c8dee2aaSAndroid Build Coastguard Worker         std::tuple<SkISize, SkColorType> calculateTextureParameters(
805*c8dee2aaSAndroid Build Coastguard Worker                 int index, const ResourceDesc& r) const override {
806*c8dee2aaSAndroid Build Coastguard Worker             return {{kDim, kDim}, kRGBA_8888_SkColorType};
807*c8dee2aaSAndroid Build Coastguard Worker         }
808*c8dee2aaSAndroid Build Coastguard Worker 
809*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
810*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
811*c8dee2aaSAndroid Build Coastguard Worker         }
812*c8dee2aaSAndroid Build Coastguard Worker     } step;
813*c8dee2aaSAndroid Build Coastguard Worker 
814*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
815*c8dee2aaSAndroid Build Coastguard Worker     if (!builder.appendStep(&step)) {
816*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to add ComputeStep to DispatchGroup");
817*c8dee2aaSAndroid Build Coastguard Worker         return;
818*c8dee2aaSAndroid Build Coastguard Worker     }
819*c8dee2aaSAndroid Build Coastguard Worker 
820*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<TextureProxy> texture = builder.getSharedTextureResource(0);
821*c8dee2aaSAndroid Build Coastguard Worker     if (!texture) {
822*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Shared resource at slot 0 is missing");
823*c8dee2aaSAndroid Build Coastguard Worker         return;
824*c8dee2aaSAndroid Build Coastguard Worker     }
825*c8dee2aaSAndroid Build Coastguard Worker 
826*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
827*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
828*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
829*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
830*c8dee2aaSAndroid Build Coastguard Worker 
831*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
832*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
833*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
834*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
835*c8dee2aaSAndroid Build Coastguard Worker         return;
836*c8dee2aaSAndroid Build Coastguard Worker     }
837*c8dee2aaSAndroid Build Coastguard Worker 
838*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
839*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
840*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
841*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
842*c8dee2aaSAndroid Build Coastguard Worker 
843*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bitmap;
844*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo imgInfo =
845*c8dee2aaSAndroid Build Coastguard Worker             SkImageInfo::Make(kDim, kDim, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
846*c8dee2aaSAndroid Build Coastguard Worker     bitmap.allocPixels(imgInfo);
847*c8dee2aaSAndroid Build Coastguard Worker 
848*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap pixels;
849*c8dee2aaSAndroid Build Coastguard Worker     bool peekPixelsSuccess = bitmap.peekPixels(&pixels);
850*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, peekPixelsSuccess);
851*c8dee2aaSAndroid Build Coastguard Worker 
852*c8dee2aaSAndroid Build Coastguard Worker     bool readPixelsSuccess = context->priv().readPixels(pixels, texture.get(), imgInfo, 0, 0);
853*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, readPixelsSuccess);
854*c8dee2aaSAndroid Build Coastguard Worker 
855*c8dee2aaSAndroid Build Coastguard Worker     for (uint32_t x = 0; x < kDim; ++x) {
856*c8dee2aaSAndroid Build Coastguard Worker         for (uint32_t y = 0; y < kDim; ++y) {
857*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f expected = SkColor4f::FromColor(SK_ColorGREEN);
858*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f color = pixels.getColor4f(x, y);
859*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, expected == color,
860*c8dee2aaSAndroid Build Coastguard Worker                             "At position {%u, %u}, "
861*c8dee2aaSAndroid Build Coastguard Worker                             "expected {%.1f, %.1f, %.1f, %.1f}, "
862*c8dee2aaSAndroid Build Coastguard Worker                             "found {%.1f, %.1f, %.1f, %.1f}",
863*c8dee2aaSAndroid Build Coastguard Worker                             x, y,
864*c8dee2aaSAndroid Build Coastguard Worker                             expected.fR, expected.fG, expected.fB, expected.fA,
865*c8dee2aaSAndroid Build Coastguard Worker                             color.fR, color.fG, color.fB, color.fA);
866*c8dee2aaSAndroid Build Coastguard Worker         }
867*c8dee2aaSAndroid Build Coastguard Worker     }
868*c8dee2aaSAndroid Build Coastguard Worker }
869*c8dee2aaSAndroid Build Coastguard Worker 
870*c8dee2aaSAndroid Build Coastguard Worker // Tests the readonly texture binding for a compute dispatch that random-access reads from a
871*c8dee2aaSAndroid Build Coastguard Worker // CPU-populated texture and copies it to a storage texture.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_StorageTextureReadAndWrite,reporter,context,testContext)872*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_StorageTextureReadAndWrite,
873*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
874*c8dee2aaSAndroid Build Coastguard Worker                                               context,
875*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
876*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
877*c8dee2aaSAndroid Build Coastguard Worker 
878*c8dee2aaSAndroid Build Coastguard Worker     // For this test we allocate a 8x8 tile which is written to by a single workgroup of the same
879*c8dee2aaSAndroid Build Coastguard Worker     // size.
880*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kDim = 8;
881*c8dee2aaSAndroid Build Coastguard Worker 
882*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
883*c8dee2aaSAndroid Build Coastguard Worker     public:
884*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
885*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestStorageTextureReadAndWrite",
886*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kDim, kDim, 1},
887*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
888*c8dee2aaSAndroid Build Coastguard Worker                     {
889*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kReadOnlyTexture,
890*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
891*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
892*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
893*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"src",
894*c8dee2aaSAndroid Build Coastguard Worker                     },
895*c8dee2aaSAndroid Build Coastguard Worker                     {
896*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kWriteOnlyStorageTexture,
897*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
898*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
899*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/1,
900*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"dst",
901*c8dee2aaSAndroid Build Coastguard Worker                     }
902*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
903*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
904*c8dee2aaSAndroid Build Coastguard Worker 
905*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
906*c8dee2aaSAndroid Build Coastguard Worker             return R"(
907*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
908*c8dee2aaSAndroid Build Coastguard Worker                     half4 color = textureRead(src, sk_LocalInvocationID.xy);
909*c8dee2aaSAndroid Build Coastguard Worker                     textureWrite(dst, sk_LocalInvocationID.xy, color);
910*c8dee2aaSAndroid Build Coastguard Worker                 }
911*c8dee2aaSAndroid Build Coastguard Worker             )";
912*c8dee2aaSAndroid Build Coastguard Worker         }
913*c8dee2aaSAndroid Build Coastguard Worker 
914*c8dee2aaSAndroid Build Coastguard Worker         std::tuple<SkISize, SkColorType> calculateTextureParameters(
915*c8dee2aaSAndroid Build Coastguard Worker                 int index, const ResourceDesc& r) const override {
916*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 1);
917*c8dee2aaSAndroid Build Coastguard Worker             return {{kDim, kDim}, kRGBA_8888_SkColorType};
918*c8dee2aaSAndroid Build Coastguard Worker         }
919*c8dee2aaSAndroid Build Coastguard Worker 
920*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
921*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
922*c8dee2aaSAndroid Build Coastguard Worker         }
923*c8dee2aaSAndroid Build Coastguard Worker     } step;
924*c8dee2aaSAndroid Build Coastguard Worker 
925*c8dee2aaSAndroid Build Coastguard Worker     // Create and populate an input texture.
926*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap srcBitmap;
927*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo srcInfo =
928*c8dee2aaSAndroid Build Coastguard Worker             SkImageInfo::Make(kDim, kDim, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
929*c8dee2aaSAndroid Build Coastguard Worker     srcBitmap.allocPixels(srcInfo);
930*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap srcPixels;
931*c8dee2aaSAndroid Build Coastguard Worker     bool srcPeekPixelsSuccess = srcBitmap.peekPixels(&srcPixels);
932*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, srcPeekPixelsSuccess);
933*c8dee2aaSAndroid Build Coastguard Worker     for (uint32_t x = 0; x < kDim; ++x) {
934*c8dee2aaSAndroid Build Coastguard Worker         for (uint32_t y = 0; y < kDim; ++y) {
935*c8dee2aaSAndroid Build Coastguard Worker             *srcPixels.writable_addr32(x, y) =
936*c8dee2aaSAndroid Build Coastguard Worker                     SkColorSetARGB(255, x * 256 / kDim, y * 256 / kDim, 0);
937*c8dee2aaSAndroid Build Coastguard Worker         }
938*c8dee2aaSAndroid Build Coastguard Worker     }
939*c8dee2aaSAndroid Build Coastguard Worker 
940*c8dee2aaSAndroid Build Coastguard Worker     auto texInfo = context->priv().caps()->getDefaultSampledTextureInfo(kRGBA_8888_SkColorType,
941*c8dee2aaSAndroid Build Coastguard Worker                                                                         skgpu::Mipmapped::kNo,
942*c8dee2aaSAndroid Build Coastguard Worker                                                                         skgpu::Protected::kNo,
943*c8dee2aaSAndroid Build Coastguard Worker                                                                         skgpu::Renderable::kNo);
944*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<TextureProxy> srcProxy = TextureProxy::Make(context->priv().caps(),
945*c8dee2aaSAndroid Build Coastguard Worker                                                       recorder->priv().resourceProvider(),
946*c8dee2aaSAndroid Build Coastguard Worker                                                       {kDim, kDim},
947*c8dee2aaSAndroid Build Coastguard Worker                                                       texInfo,
948*c8dee2aaSAndroid Build Coastguard Worker                                                       "ComputeTestSrcProxy",
949*c8dee2aaSAndroid Build Coastguard Worker                                                       skgpu::Budgeted::kNo);
950*c8dee2aaSAndroid Build Coastguard Worker     MipLevel mipLevel;
951*c8dee2aaSAndroid Build Coastguard Worker     mipLevel.fPixels = srcPixels.addr();
952*c8dee2aaSAndroid Build Coastguard Worker     mipLevel.fRowBytes = srcPixels.rowBytes();
953*c8dee2aaSAndroid Build Coastguard Worker     UploadInstance upload = UploadInstance::Make(recorder.get(),
954*c8dee2aaSAndroid Build Coastguard Worker                                                  srcProxy,
955*c8dee2aaSAndroid Build Coastguard Worker                                                  srcPixels.info().colorInfo(),
956*c8dee2aaSAndroid Build Coastguard Worker                                                  srcPixels.info().colorInfo(),
957*c8dee2aaSAndroid Build Coastguard Worker                                                  {mipLevel},
958*c8dee2aaSAndroid Build Coastguard Worker                                                  SkIRect::MakeWH(kDim, kDim),
959*c8dee2aaSAndroid Build Coastguard Worker                                                  std::make_unique<ImageUploadContext>());
960*c8dee2aaSAndroid Build Coastguard Worker     if (!upload.isValid()) {
961*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Could not create UploadInstance");
962*c8dee2aaSAndroid Build Coastguard Worker         return;
963*c8dee2aaSAndroid Build Coastguard Worker     }
964*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(UploadTask::Make(std::move(upload)));
965*c8dee2aaSAndroid Build Coastguard Worker 
966*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
967*c8dee2aaSAndroid Build Coastguard Worker 
968*c8dee2aaSAndroid Build Coastguard Worker     // Assign the input texture to slot 0. This corresponds to the ComputeStep's "src" texture
969*c8dee2aaSAndroid Build Coastguard Worker     // binding.
970*c8dee2aaSAndroid Build Coastguard Worker     builder.assignSharedTexture(std::move(srcProxy), 0);
971*c8dee2aaSAndroid Build Coastguard Worker 
972*c8dee2aaSAndroid Build Coastguard Worker     if (!builder.appendStep(&step)) {
973*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to add ComputeStep to DispatchGroup");
974*c8dee2aaSAndroid Build Coastguard Worker         return;
975*c8dee2aaSAndroid Build Coastguard Worker     }
976*c8dee2aaSAndroid Build Coastguard Worker 
977*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<TextureProxy> dst = builder.getSharedTextureResource(1);
978*c8dee2aaSAndroid Build Coastguard Worker     if (!dst) {
979*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 1 is missing");
980*c8dee2aaSAndroid Build Coastguard Worker         return;
981*c8dee2aaSAndroid Build Coastguard Worker     }
982*c8dee2aaSAndroid Build Coastguard Worker 
983*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
984*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
985*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
986*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
987*c8dee2aaSAndroid Build Coastguard Worker 
988*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
989*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
990*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
991*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
992*c8dee2aaSAndroid Build Coastguard Worker         return;
993*c8dee2aaSAndroid Build Coastguard Worker     }
994*c8dee2aaSAndroid Build Coastguard Worker 
995*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
996*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
997*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
998*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
999*c8dee2aaSAndroid Build Coastguard Worker 
1000*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bitmap;
1001*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo imgInfo =
1002*c8dee2aaSAndroid Build Coastguard Worker             SkImageInfo::Make(kDim, kDim, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
1003*c8dee2aaSAndroid Build Coastguard Worker     bitmap.allocPixels(imgInfo);
1004*c8dee2aaSAndroid Build Coastguard Worker 
1005*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap pixels;
1006*c8dee2aaSAndroid Build Coastguard Worker     bool peekPixelsSuccess = bitmap.peekPixels(&pixels);
1007*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, peekPixelsSuccess);
1008*c8dee2aaSAndroid Build Coastguard Worker 
1009*c8dee2aaSAndroid Build Coastguard Worker     bool readPixelsSuccess = context->priv().readPixels(pixels, dst.get(), imgInfo, 0, 0);
1010*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, readPixelsSuccess);
1011*c8dee2aaSAndroid Build Coastguard Worker 
1012*c8dee2aaSAndroid Build Coastguard Worker     for (uint32_t x = 0; x < kDim; ++x) {
1013*c8dee2aaSAndroid Build Coastguard Worker         for (uint32_t y = 0; y < kDim; ++y) {
1014*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f expected = SkColor4f::FromBytes_RGBA(
1015*c8dee2aaSAndroid Build Coastguard Worker                     SkColorSetARGB(255, x * 256 / kDim, y * 256 / kDim, 0));
1016*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f color = pixels.getColor4f(x, y);
1017*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, expected == color,
1018*c8dee2aaSAndroid Build Coastguard Worker                             "At position {%u, %u}, "
1019*c8dee2aaSAndroid Build Coastguard Worker                             "expected {%.1f, %.1f, %.1f, %.1f}, "
1020*c8dee2aaSAndroid Build Coastguard Worker                             "found {%.1f, %.1f, %.1f, %.1f}",
1021*c8dee2aaSAndroid Build Coastguard Worker                             x, y,
1022*c8dee2aaSAndroid Build Coastguard Worker                             expected.fR, expected.fG, expected.fB, expected.fA,
1023*c8dee2aaSAndroid Build Coastguard Worker                             color.fR, color.fG, color.fB, color.fA);
1024*c8dee2aaSAndroid Build Coastguard Worker         }
1025*c8dee2aaSAndroid Build Coastguard Worker     }
1026*c8dee2aaSAndroid Build Coastguard Worker }
1027*c8dee2aaSAndroid Build Coastguard Worker 
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ReadOnlyStorageBuffer,reporter,context,testContext)1028*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ReadOnlyStorageBuffer,
1029*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
1030*c8dee2aaSAndroid Build Coastguard Worker                                               context,
1031*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
1032*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
1033*c8dee2aaSAndroid Build Coastguard Worker 
1034*c8dee2aaSAndroid Build Coastguard Worker     // For this test we allocate a 8x8 tile which is written to by a single workgroup of the same
1035*c8dee2aaSAndroid Build Coastguard Worker     // size.
1036*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kDim = 8;
1037*c8dee2aaSAndroid Build Coastguard Worker 
1038*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
1039*c8dee2aaSAndroid Build Coastguard Worker     public:
1040*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
1041*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestReadOnlyStorageBuffer",
1042*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kDim, kDim, 1},
1043*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1044*c8dee2aaSAndroid Build Coastguard Worker                     {
1045*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kReadOnlyStorageBuffer,
1046*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1047*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
1048*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1049*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"src { uint in_data[]; }",
1050*c8dee2aaSAndroid Build Coastguard Worker                     },
1051*c8dee2aaSAndroid Build Coastguard Worker                     {
1052*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kWriteOnlyStorageTexture,
1053*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1054*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1055*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/1,
1056*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"dst",
1057*c8dee2aaSAndroid Build Coastguard Worker                     }
1058*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1059*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
1060*c8dee2aaSAndroid Build Coastguard Worker 
1061*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1062*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1063*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1064*c8dee2aaSAndroid Build Coastguard Worker                     uint ix = sk_LocalInvocationID.y * 8 + sk_LocalInvocationID.x;
1065*c8dee2aaSAndroid Build Coastguard Worker                     uint value = in_data[ix];
1066*c8dee2aaSAndroid Build Coastguard Worker                     half4 splat = half4(
1067*c8dee2aaSAndroid Build Coastguard Worker                         half(value & 0xFF),
1068*c8dee2aaSAndroid Build Coastguard Worker                         half((value >> 8) & 0xFF),
1069*c8dee2aaSAndroid Build Coastguard Worker                         half((value >> 16) & 0xFF),
1070*c8dee2aaSAndroid Build Coastguard Worker                         half((value >> 24) & 0xFF)
1071*c8dee2aaSAndroid Build Coastguard Worker                     );
1072*c8dee2aaSAndroid Build Coastguard Worker                     textureWrite(dst, sk_LocalInvocationID.xy, splat / 255.0);
1073*c8dee2aaSAndroid Build Coastguard Worker                 }
1074*c8dee2aaSAndroid Build Coastguard Worker             )";
1075*c8dee2aaSAndroid Build Coastguard Worker         }
1076*c8dee2aaSAndroid Build Coastguard Worker 
1077*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
1078*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
1079*c8dee2aaSAndroid Build Coastguard Worker             return kDim * kDim * sizeof(uint32_t);
1080*c8dee2aaSAndroid Build Coastguard Worker         }
1081*c8dee2aaSAndroid Build Coastguard Worker 
1082*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int index,
1083*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc&,
1084*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
1085*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
1086*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
1087*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(bufferSize == kDim * kDim * sizeof(uint32_t));
1088*c8dee2aaSAndroid Build Coastguard Worker 
1089*c8dee2aaSAndroid Build Coastguard Worker             uint32_t* inputs = reinterpret_cast<uint32_t*>(buffer);
1090*c8dee2aaSAndroid Build Coastguard Worker             for (uint32_t y = 0; y < kDim; ++y) {
1091*c8dee2aaSAndroid Build Coastguard Worker                 for (uint32_t x = 0; x < kDim; ++x) {
1092*c8dee2aaSAndroid Build Coastguard Worker                     uint32_t value =
1093*c8dee2aaSAndroid Build Coastguard Worker                             ((x * 256 / kDim) & 0xFF) | ((y * 256 / kDim) & 0xFF) << 8 | 255 << 24;
1094*c8dee2aaSAndroid Build Coastguard Worker                     *(inputs++) = value;
1095*c8dee2aaSAndroid Build Coastguard Worker                 }
1096*c8dee2aaSAndroid Build Coastguard Worker             }
1097*c8dee2aaSAndroid Build Coastguard Worker         }
1098*c8dee2aaSAndroid Build Coastguard Worker 
1099*c8dee2aaSAndroid Build Coastguard Worker         std::tuple<SkISize, SkColorType> calculateTextureParameters(
1100*c8dee2aaSAndroid Build Coastguard Worker                 int index, const ResourceDesc& r) const override {
1101*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 1);
1102*c8dee2aaSAndroid Build Coastguard Worker             return {{kDim, kDim}, kRGBA_8888_SkColorType};
1103*c8dee2aaSAndroid Build Coastguard Worker         }
1104*c8dee2aaSAndroid Build Coastguard Worker 
1105*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
1106*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
1107*c8dee2aaSAndroid Build Coastguard Worker         }
1108*c8dee2aaSAndroid Build Coastguard Worker     } step;
1109*c8dee2aaSAndroid Build Coastguard Worker 
1110*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
1111*c8dee2aaSAndroid Build Coastguard Worker     if (!builder.appendStep(&step)) {
1112*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to add ComputeStep to DispatchGroup");
1113*c8dee2aaSAndroid Build Coastguard Worker         return;
1114*c8dee2aaSAndroid Build Coastguard Worker     }
1115*c8dee2aaSAndroid Build Coastguard Worker 
1116*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<TextureProxy> dst = builder.getSharedTextureResource(1);
1117*c8dee2aaSAndroid Build Coastguard Worker     if (!dst) {
1118*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 1 is missing");
1119*c8dee2aaSAndroid Build Coastguard Worker         return;
1120*c8dee2aaSAndroid Build Coastguard Worker     }
1121*c8dee2aaSAndroid Build Coastguard Worker 
1122*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
1123*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
1124*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
1125*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
1126*c8dee2aaSAndroid Build Coastguard Worker 
1127*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
1128*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
1129*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
1130*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
1131*c8dee2aaSAndroid Build Coastguard Worker         return;
1132*c8dee2aaSAndroid Build Coastguard Worker     }
1133*c8dee2aaSAndroid Build Coastguard Worker 
1134*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
1135*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
1136*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
1137*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
1138*c8dee2aaSAndroid Build Coastguard Worker 
1139*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bitmap;
1140*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo imgInfo =
1141*c8dee2aaSAndroid Build Coastguard Worker             SkImageInfo::Make(kDim, kDim, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
1142*c8dee2aaSAndroid Build Coastguard Worker     bitmap.allocPixels(imgInfo);
1143*c8dee2aaSAndroid Build Coastguard Worker 
1144*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap pixels;
1145*c8dee2aaSAndroid Build Coastguard Worker     bool peekPixelsSuccess = bitmap.peekPixels(&pixels);
1146*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, peekPixelsSuccess);
1147*c8dee2aaSAndroid Build Coastguard Worker 
1148*c8dee2aaSAndroid Build Coastguard Worker     bool readPixelsSuccess = context->priv().readPixels(pixels, dst.get(), imgInfo, 0, 0);
1149*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, readPixelsSuccess);
1150*c8dee2aaSAndroid Build Coastguard Worker 
1151*c8dee2aaSAndroid Build Coastguard Worker     for (uint32_t x = 0; x < kDim; ++x) {
1152*c8dee2aaSAndroid Build Coastguard Worker         for (uint32_t y = 0; y < kDim; ++y) {
1153*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f expected =
1154*c8dee2aaSAndroid Build Coastguard Worker                     SkColor4f::FromColor(SkColorSetARGB(255, x * 256 / kDim, y * 256 / kDim, 0));
1155*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f color = pixels.getColor4f(x, y);
1156*c8dee2aaSAndroid Build Coastguard Worker             bool pass = true;
1157*c8dee2aaSAndroid Build Coastguard Worker             for (int i = 0; i < 4; i++) {
1158*c8dee2aaSAndroid Build Coastguard Worker                 pass &= color[i] == expected[i];
1159*c8dee2aaSAndroid Build Coastguard Worker             }
1160*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, pass,
1161*c8dee2aaSAndroid Build Coastguard Worker                             "At position {%u, %u}, "
1162*c8dee2aaSAndroid Build Coastguard Worker                             "expected {%.1f, %.1f, %.1f, %.1f}, "
1163*c8dee2aaSAndroid Build Coastguard Worker                             "found {%.1f, %.1f, %.1f, %.1f}",
1164*c8dee2aaSAndroid Build Coastguard Worker                             x, y,
1165*c8dee2aaSAndroid Build Coastguard Worker                             expected.fR, expected.fG, expected.fB, expected.fA,
1166*c8dee2aaSAndroid Build Coastguard Worker                             color.fR, color.fG, color.fB, color.fA);
1167*c8dee2aaSAndroid Build Coastguard Worker         }
1168*c8dee2aaSAndroid Build Coastguard Worker     }
1169*c8dee2aaSAndroid Build Coastguard Worker }
1170*c8dee2aaSAndroid Build Coastguard Worker 
1171*c8dee2aaSAndroid Build Coastguard Worker // Tests that a texture written by one compute step can be sampled by a subsequent step.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_StorageTextureMultipleComputeSteps,reporter,context,testContext)1172*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_StorageTextureMultipleComputeSteps,
1173*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
1174*c8dee2aaSAndroid Build Coastguard Worker                                               context,
1175*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
1176*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
1177*c8dee2aaSAndroid Build Coastguard Worker 
1178*c8dee2aaSAndroid Build Coastguard Worker     // For this test we allocate a 8x8 tile which is written to by a single workgroup of the same
1179*c8dee2aaSAndroid Build Coastguard Worker     // size.
1180*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kDim = 8;
1181*c8dee2aaSAndroid Build Coastguard Worker 
1182*c8dee2aaSAndroid Build Coastguard Worker     // Writes to a texture in slot 0.
1183*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep1 : public ComputeStep {
1184*c8dee2aaSAndroid Build Coastguard Worker     public:
1185*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep1() : ComputeStep(
1186*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestStorageTexturesFirstPass",
1187*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kDim, kDim, 1},
1188*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1189*c8dee2aaSAndroid Build Coastguard Worker                     {
1190*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kWriteOnlyStorageTexture,
1191*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1192*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1193*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1194*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"dst",
1195*c8dee2aaSAndroid Build Coastguard Worker                     }
1196*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1197*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep1() override = default;
1198*c8dee2aaSAndroid Build Coastguard Worker 
1199*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1200*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1201*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1202*c8dee2aaSAndroid Build Coastguard Worker                     textureWrite(dst, sk_LocalInvocationID.xy, half4(0.0, 1.0, 0.0, 1.0));
1203*c8dee2aaSAndroid Build Coastguard Worker                 }
1204*c8dee2aaSAndroid Build Coastguard Worker             )";
1205*c8dee2aaSAndroid Build Coastguard Worker         }
1206*c8dee2aaSAndroid Build Coastguard Worker 
1207*c8dee2aaSAndroid Build Coastguard Worker         std::tuple<SkISize, SkColorType> calculateTextureParameters(
1208*c8dee2aaSAndroid Build Coastguard Worker                 int index, const ResourceDesc& r) const override {
1209*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
1210*c8dee2aaSAndroid Build Coastguard Worker             return {{kDim, kDim}, kRGBA_8888_SkColorType};
1211*c8dee2aaSAndroid Build Coastguard Worker         }
1212*c8dee2aaSAndroid Build Coastguard Worker 
1213*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
1214*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
1215*c8dee2aaSAndroid Build Coastguard Worker         }
1216*c8dee2aaSAndroid Build Coastguard Worker     } step1;
1217*c8dee2aaSAndroid Build Coastguard Worker 
1218*c8dee2aaSAndroid Build Coastguard Worker     // Reads from the texture in slot 0 and writes it to another texture in slot 1.
1219*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep2 : public ComputeStep {
1220*c8dee2aaSAndroid Build Coastguard Worker     public:
1221*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep2() : ComputeStep(
1222*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestStorageTexturesSecondPass",
1223*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kDim, kDim, 1},
1224*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1225*c8dee2aaSAndroid Build Coastguard Worker                     {
1226*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kReadOnlyTexture,
1227*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1228*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1229*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1230*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"src",
1231*c8dee2aaSAndroid Build Coastguard Worker                     },
1232*c8dee2aaSAndroid Build Coastguard Worker                     {
1233*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kWriteOnlyStorageTexture,
1234*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1235*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1236*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/1,
1237*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"dst",
1238*c8dee2aaSAndroid Build Coastguard Worker                     }
1239*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1240*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep2() override = default;
1241*c8dee2aaSAndroid Build Coastguard Worker 
1242*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1243*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1244*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1245*c8dee2aaSAndroid Build Coastguard Worker                     half4 color = textureRead(src, sk_LocalInvocationID.xy);
1246*c8dee2aaSAndroid Build Coastguard Worker                     textureWrite(dst, sk_LocalInvocationID.xy, color);
1247*c8dee2aaSAndroid Build Coastguard Worker                 }
1248*c8dee2aaSAndroid Build Coastguard Worker             )";
1249*c8dee2aaSAndroid Build Coastguard Worker         }
1250*c8dee2aaSAndroid Build Coastguard Worker 
1251*c8dee2aaSAndroid Build Coastguard Worker         std::tuple<SkISize, SkColorType> calculateTextureParameters(
1252*c8dee2aaSAndroid Build Coastguard Worker                 int index, const ResourceDesc& r) const override {
1253*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 1);
1254*c8dee2aaSAndroid Build Coastguard Worker             return {{kDim, kDim}, kRGBA_8888_SkColorType};
1255*c8dee2aaSAndroid Build Coastguard Worker         }
1256*c8dee2aaSAndroid Build Coastguard Worker 
1257*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
1258*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
1259*c8dee2aaSAndroid Build Coastguard Worker         }
1260*c8dee2aaSAndroid Build Coastguard Worker     } step2;
1261*c8dee2aaSAndroid Build Coastguard Worker 
1262*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
1263*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step1);
1264*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step2);
1265*c8dee2aaSAndroid Build Coastguard Worker 
1266*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<TextureProxy> dst = builder.getSharedTextureResource(1);
1267*c8dee2aaSAndroid Build Coastguard Worker     if (!dst) {
1268*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 1 is missing");
1269*c8dee2aaSAndroid Build Coastguard Worker         return;
1270*c8dee2aaSAndroid Build Coastguard Worker     }
1271*c8dee2aaSAndroid Build Coastguard Worker 
1272*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
1273*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
1274*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
1275*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
1276*c8dee2aaSAndroid Build Coastguard Worker 
1277*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
1278*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
1279*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
1280*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
1281*c8dee2aaSAndroid Build Coastguard Worker         return;
1282*c8dee2aaSAndroid Build Coastguard Worker     }
1283*c8dee2aaSAndroid Build Coastguard Worker 
1284*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
1285*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
1286*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
1287*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
1288*c8dee2aaSAndroid Build Coastguard Worker 
1289*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bitmap;
1290*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo imgInfo =
1291*c8dee2aaSAndroid Build Coastguard Worker             SkImageInfo::Make(kDim, kDim, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
1292*c8dee2aaSAndroid Build Coastguard Worker     bitmap.allocPixels(imgInfo);
1293*c8dee2aaSAndroid Build Coastguard Worker 
1294*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap pixels;
1295*c8dee2aaSAndroid Build Coastguard Worker     bool peekPixelsSuccess = bitmap.peekPixels(&pixels);
1296*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, peekPixelsSuccess);
1297*c8dee2aaSAndroid Build Coastguard Worker 
1298*c8dee2aaSAndroid Build Coastguard Worker     bool readPixelsSuccess = context->priv().readPixels(pixels, dst.get(), imgInfo, 0, 0);
1299*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, readPixelsSuccess);
1300*c8dee2aaSAndroid Build Coastguard Worker 
1301*c8dee2aaSAndroid Build Coastguard Worker     for (uint32_t x = 0; x < kDim; ++x) {
1302*c8dee2aaSAndroid Build Coastguard Worker         for (uint32_t y = 0; y < kDim; ++y) {
1303*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f expected = SkColor4f::FromColor(SK_ColorGREEN);
1304*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f color = pixels.getColor4f(x, y);
1305*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, expected == color,
1306*c8dee2aaSAndroid Build Coastguard Worker                             "At position {%u, %u}, "
1307*c8dee2aaSAndroid Build Coastguard Worker                             "expected {%.1f, %.1f, %.1f, %.1f}, "
1308*c8dee2aaSAndroid Build Coastguard Worker                             "found {%.1f, %.1f, %.1f, %.1f}",
1309*c8dee2aaSAndroid Build Coastguard Worker                             x, y,
1310*c8dee2aaSAndroid Build Coastguard Worker                             expected.fR, expected.fG, expected.fB, expected.fA,
1311*c8dee2aaSAndroid Build Coastguard Worker                             color.fR, color.fG, color.fB, color.fA);
1312*c8dee2aaSAndroid Build Coastguard Worker         }
1313*c8dee2aaSAndroid Build Coastguard Worker     }
1314*c8dee2aaSAndroid Build Coastguard Worker }
1315*c8dee2aaSAndroid Build Coastguard Worker 
1316*c8dee2aaSAndroid Build Coastguard Worker // Tests that a texture can be sampled by a compute step using a sampler.
1317*c8dee2aaSAndroid Build Coastguard Worker // TODO(armansito): Once the previous TODO is done, add additional tests that exercise mixed use of
1318*c8dee2aaSAndroid Build Coastguard Worker // texture, buffer, and sampler bindings.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_SampledTexture,reporter,context,testContext)1319*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_SampledTexture,
1320*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
1321*c8dee2aaSAndroid Build Coastguard Worker                                               context,
1322*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
1323*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
1324*c8dee2aaSAndroid Build Coastguard Worker 
1325*c8dee2aaSAndroid Build Coastguard Worker     // The first ComputeStep initializes a 8x8 texture with a checkerboard pattern of alternating
1326*c8dee2aaSAndroid Build Coastguard Worker     // red and black pixels. The second ComputeStep downsamples this texture into a 4x4 using
1327*c8dee2aaSAndroid Build Coastguard Worker     // bilinear filtering at pixel borders, intentionally averaging the values of each 4x4 tile in
1328*c8dee2aaSAndroid Build Coastguard Worker     // the source texture, and writes the result to the destination texture.
1329*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kSrcDim = 8;
1330*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kDstDim = 4;
1331*c8dee2aaSAndroid Build Coastguard Worker 
1332*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep1 : public ComputeStep {
1333*c8dee2aaSAndroid Build Coastguard Worker     public:
1334*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep1() : ComputeStep(
1335*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"Test_SampledTexture_Init",
1336*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kSrcDim, kSrcDim, 1},
1337*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1338*c8dee2aaSAndroid Build Coastguard Worker                     {
1339*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kWriteOnlyStorageTexture,
1340*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1341*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1342*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1343*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"dst",
1344*c8dee2aaSAndroid Build Coastguard Worker                     }
1345*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1346*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep1() override = default;
1347*c8dee2aaSAndroid Build Coastguard Worker 
1348*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1349*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1350*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1351*c8dee2aaSAndroid Build Coastguard Worker                     uint2 c = sk_LocalInvocationID.xy;
1352*c8dee2aaSAndroid Build Coastguard Worker                     uint checkerBoardColor = (c.x + (c.y % 2)) % 2;
1353*c8dee2aaSAndroid Build Coastguard Worker                     textureWrite(dst, c, half4(checkerBoardColor, 0, 0, 1));
1354*c8dee2aaSAndroid Build Coastguard Worker                 }
1355*c8dee2aaSAndroid Build Coastguard Worker             )";
1356*c8dee2aaSAndroid Build Coastguard Worker         }
1357*c8dee2aaSAndroid Build Coastguard Worker 
1358*c8dee2aaSAndroid Build Coastguard Worker         std::tuple<SkISize, SkColorType> calculateTextureParameters(
1359*c8dee2aaSAndroid Build Coastguard Worker                 int index, const ResourceDesc& r) const override {
1360*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
1361*c8dee2aaSAndroid Build Coastguard Worker             return {{kSrcDim, kSrcDim}, kRGBA_8888_SkColorType};
1362*c8dee2aaSAndroid Build Coastguard Worker         }
1363*c8dee2aaSAndroid Build Coastguard Worker 
1364*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
1365*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
1366*c8dee2aaSAndroid Build Coastguard Worker         }
1367*c8dee2aaSAndroid Build Coastguard Worker     } step1;
1368*c8dee2aaSAndroid Build Coastguard Worker 
1369*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep2 : public ComputeStep {
1370*c8dee2aaSAndroid Build Coastguard Worker     public:
1371*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep2() : ComputeStep(
1372*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"Test_SampledTexture_Sample",
1373*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kDstDim, kDstDim, 1},
1374*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1375*c8dee2aaSAndroid Build Coastguard Worker                     // Declare the storage texture before the sampled texture. This tests that
1376*c8dee2aaSAndroid Build Coastguard Worker                     // binding index assignment works consistently across all backends when a
1377*c8dee2aaSAndroid Build Coastguard Worker                     // sampler-less texture and a texture+sampler pair are intermixed and sampler
1378*c8dee2aaSAndroid Build Coastguard Worker                     // bindings aren't necessarily contiguous when the ranges are distinct.
1379*c8dee2aaSAndroid Build Coastguard Worker                     {
1380*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kWriteOnlyStorageTexture,
1381*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1382*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1383*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/1,
1384*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"dst",
1385*c8dee2aaSAndroid Build Coastguard Worker                     },
1386*c8dee2aaSAndroid Build Coastguard Worker                     {
1387*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kSampledTexture,
1388*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1389*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1390*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1391*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"src",
1392*c8dee2aaSAndroid Build Coastguard Worker                     }
1393*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1394*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep2() override = default;
1395*c8dee2aaSAndroid Build Coastguard Worker 
1396*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1397*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1398*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1399*c8dee2aaSAndroid Build Coastguard Worker                     // Normalize the 4x4 invocation indices and sample the source texture using
1400*c8dee2aaSAndroid Build Coastguard Worker                     // that.
1401*c8dee2aaSAndroid Build Coastguard Worker                     uint2 dstCoord = sk_LocalInvocationID.xy;
1402*c8dee2aaSAndroid Build Coastguard Worker                     const float2 dstSizeInv = float2(0.25, 0.25);
1403*c8dee2aaSAndroid Build Coastguard Worker                     float2 unormCoord = float2(dstCoord) * dstSizeInv;
1404*c8dee2aaSAndroid Build Coastguard Worker 
1405*c8dee2aaSAndroid Build Coastguard Worker                     // Use explicit LOD, as quad derivatives are not available to a compute shader.
1406*c8dee2aaSAndroid Build Coastguard Worker                     half4 color = sampleLod(src, unormCoord, 0);
1407*c8dee2aaSAndroid Build Coastguard Worker                     textureWrite(dst, dstCoord, color);
1408*c8dee2aaSAndroid Build Coastguard Worker                 }
1409*c8dee2aaSAndroid Build Coastguard Worker             )";
1410*c8dee2aaSAndroid Build Coastguard Worker         }
1411*c8dee2aaSAndroid Build Coastguard Worker 
1412*c8dee2aaSAndroid Build Coastguard Worker         std::tuple<SkISize, SkColorType> calculateTextureParameters(
1413*c8dee2aaSAndroid Build Coastguard Worker                 int index, const ResourceDesc& r) const override {
1414*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0 || index == 1);
1415*c8dee2aaSAndroid Build Coastguard Worker             return {{kDstDim, kDstDim}, kRGBA_8888_SkColorType};
1416*c8dee2aaSAndroid Build Coastguard Worker         }
1417*c8dee2aaSAndroid Build Coastguard Worker 
1418*c8dee2aaSAndroid Build Coastguard Worker         SamplerDesc calculateSamplerParameters(int index, const ResourceDesc&) const override {
1419*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 1);
1420*c8dee2aaSAndroid Build Coastguard Worker             // Use the repeat tile mode to sample an infinite checkerboard.
1421*c8dee2aaSAndroid Build Coastguard Worker             return {SkFilterMode::kLinear, SkTileMode::kRepeat};
1422*c8dee2aaSAndroid Build Coastguard Worker         }
1423*c8dee2aaSAndroid Build Coastguard Worker 
1424*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
1425*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
1426*c8dee2aaSAndroid Build Coastguard Worker         }
1427*c8dee2aaSAndroid Build Coastguard Worker     } step2;
1428*c8dee2aaSAndroid Build Coastguard Worker 
1429*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
1430*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step1);
1431*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step2);
1432*c8dee2aaSAndroid Build Coastguard Worker 
1433*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<TextureProxy> dst = builder.getSharedTextureResource(1);
1434*c8dee2aaSAndroid Build Coastguard Worker     if (!dst) {
1435*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 1 is missing");
1436*c8dee2aaSAndroid Build Coastguard Worker         return;
1437*c8dee2aaSAndroid Build Coastguard Worker     }
1438*c8dee2aaSAndroid Build Coastguard Worker 
1439*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
1440*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
1441*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
1442*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
1443*c8dee2aaSAndroid Build Coastguard Worker 
1444*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
1445*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
1446*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
1447*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
1448*c8dee2aaSAndroid Build Coastguard Worker         return;
1449*c8dee2aaSAndroid Build Coastguard Worker     }
1450*c8dee2aaSAndroid Build Coastguard Worker 
1451*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
1452*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
1453*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
1454*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
1455*c8dee2aaSAndroid Build Coastguard Worker 
1456*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bitmap;
1457*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo imgInfo =
1458*c8dee2aaSAndroid Build Coastguard Worker             SkImageInfo::Make(kDstDim, kDstDim, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
1459*c8dee2aaSAndroid Build Coastguard Worker     bitmap.allocPixels(imgInfo);
1460*c8dee2aaSAndroid Build Coastguard Worker 
1461*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap pixels;
1462*c8dee2aaSAndroid Build Coastguard Worker     bool peekPixelsSuccess = bitmap.peekPixels(&pixels);
1463*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, peekPixelsSuccess);
1464*c8dee2aaSAndroid Build Coastguard Worker 
1465*c8dee2aaSAndroid Build Coastguard Worker     bool readPixelsSuccess = context->priv().readPixels(pixels, dst.get(), imgInfo, 0, 0);
1466*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, readPixelsSuccess);
1467*c8dee2aaSAndroid Build Coastguard Worker 
1468*c8dee2aaSAndroid Build Coastguard Worker     for (uint32_t x = 0; x < kDstDim; ++x) {
1469*c8dee2aaSAndroid Build Coastguard Worker         for (uint32_t y = 0; y < kDstDim; ++y) {
1470*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f color = pixels.getColor4f(x, y);
1471*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, color.fR > 0.49 && color.fR < 0.51,
1472*c8dee2aaSAndroid Build Coastguard Worker                             "At position {%u, %u}, "
1473*c8dee2aaSAndroid Build Coastguard Worker                             "expected red channel in range [0.49, 0.51], "
1474*c8dee2aaSAndroid Build Coastguard Worker                             "found {%.3f}",
1475*c8dee2aaSAndroid Build Coastguard Worker                             x, y, color.fR);
1476*c8dee2aaSAndroid Build Coastguard Worker         }
1477*c8dee2aaSAndroid Build Coastguard Worker     }
1478*c8dee2aaSAndroid Build Coastguard Worker }
1479*c8dee2aaSAndroid Build Coastguard Worker 
1480*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/260622403): The shader tested here is identical to
1481*c8dee2aaSAndroid Build Coastguard Worker // `resources/sksl/compute/AtomicsOperations.compute`. It would be nice to be able to exercise SkSL
1482*c8dee2aaSAndroid Build Coastguard Worker // features like this as part of SkSLTest.cpp instead of as a graphite test.
1483*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/262427430, b/262429132): Enable this test on other backends once they all support
1484*c8dee2aaSAndroid Build Coastguard Worker // compute programs.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_AtomicOperationsTest,reporter,context,testContext)1485*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_AtomicOperationsTest,
1486*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
1487*c8dee2aaSAndroid Build Coastguard Worker                                               context,
1488*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
1489*c8dee2aaSAndroid Build Coastguard Worker     // This fails on Dawn D3D11, b/315834710
1490*c8dee2aaSAndroid Build Coastguard Worker     if (testContext->contextType() == skgpu::ContextType::kDawn_D3D11) {
1491*c8dee2aaSAndroid Build Coastguard Worker         return;
1492*c8dee2aaSAndroid Build Coastguard Worker     }
1493*c8dee2aaSAndroid Build Coastguard Worker 
1494*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
1495*c8dee2aaSAndroid Build Coastguard Worker 
1496*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupCount = 32;
1497*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = 128;
1498*c8dee2aaSAndroid Build Coastguard Worker 
1499*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
1500*c8dee2aaSAndroid Build Coastguard Worker     public:
1501*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
1502*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestAtomicOperations",
1503*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
1504*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1505*c8dee2aaSAndroid Build Coastguard Worker                     {
1506*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
1507*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1508*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
1509*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1510*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"ssbo { atomicUint globalCounter; }",
1511*c8dee2aaSAndroid Build Coastguard Worker                     }
1512*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1513*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
1514*c8dee2aaSAndroid Build Coastguard Worker 
1515*c8dee2aaSAndroid Build Coastguard Worker         // A kernel that increments a global (device memory) counter across multiple workgroups.
1516*c8dee2aaSAndroid Build Coastguard Worker         // Each workgroup maintains its own independent tally in a workgroup-shared counter which
1517*c8dee2aaSAndroid Build Coastguard Worker         // is then added to the global count.
1518*c8dee2aaSAndroid Build Coastguard Worker         //
1519*c8dee2aaSAndroid Build Coastguard Worker         // This exercises atomic store/load/add and coherent reads and writes over memory in storage
1520*c8dee2aaSAndroid Build Coastguard Worker         // and workgroup address spaces.
1521*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1522*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1523*c8dee2aaSAndroid Build Coastguard Worker                 workgroup atomicUint localCounter;
1524*c8dee2aaSAndroid Build Coastguard Worker 
1525*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1526*c8dee2aaSAndroid Build Coastguard Worker                     // Initialize the local counter.
1527*c8dee2aaSAndroid Build Coastguard Worker                     if (sk_LocalInvocationID.x == 0) {
1528*c8dee2aaSAndroid Build Coastguard Worker                         atomicStore(localCounter, 0);
1529*c8dee2aaSAndroid Build Coastguard Worker                     }
1530*c8dee2aaSAndroid Build Coastguard Worker 
1531*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads in the workgroup so they all see the initial value.
1532*c8dee2aaSAndroid Build Coastguard Worker                     workgroupBarrier();
1533*c8dee2aaSAndroid Build Coastguard Worker 
1534*c8dee2aaSAndroid Build Coastguard Worker                     // All threads increment the counter.
1535*c8dee2aaSAndroid Build Coastguard Worker                     atomicAdd(localCounter, 1);
1536*c8dee2aaSAndroid Build Coastguard Worker 
1537*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads again to ensure they have all executed the increment
1538*c8dee2aaSAndroid Build Coastguard Worker                     // and the following load reads the same value across all threads in the
1539*c8dee2aaSAndroid Build Coastguard Worker                     // workgroup.
1540*c8dee2aaSAndroid Build Coastguard Worker                     workgroupBarrier();
1541*c8dee2aaSAndroid Build Coastguard Worker 
1542*c8dee2aaSAndroid Build Coastguard Worker                     // Add the workgroup-only tally to the global counter.
1543*c8dee2aaSAndroid Build Coastguard Worker                     if (sk_LocalInvocationID.x == 0) {
1544*c8dee2aaSAndroid Build Coastguard Worker                         atomicAdd(globalCounter, atomicLoad(localCounter));
1545*c8dee2aaSAndroid Build Coastguard Worker                     }
1546*c8dee2aaSAndroid Build Coastguard Worker                 }
1547*c8dee2aaSAndroid Build Coastguard Worker             )";
1548*c8dee2aaSAndroid Build Coastguard Worker         }
1549*c8dee2aaSAndroid Build Coastguard Worker 
1550*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
1551*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
1552*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 0);
1553*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
1554*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(uint32_t);
1555*c8dee2aaSAndroid Build Coastguard Worker         }
1556*c8dee2aaSAndroid Build Coastguard Worker 
1557*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
1558*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(kWorkgroupCount, 1, 1);
1559*c8dee2aaSAndroid Build Coastguard Worker         }
1560*c8dee2aaSAndroid Build Coastguard Worker 
1561*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
1562*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
1563*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
1564*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
1565*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 0);
1566*c8dee2aaSAndroid Build Coastguard Worker             *static_cast<uint32_t*>(buffer) = 0;
1567*c8dee2aaSAndroid Build Coastguard Worker         }
1568*c8dee2aaSAndroid Build Coastguard Worker     } step;
1569*c8dee2aaSAndroid Build Coastguard Worker 
1570*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
1571*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step);
1572*c8dee2aaSAndroid Build Coastguard Worker 
1573*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo info = builder.getSharedBufferResource(0);
1574*c8dee2aaSAndroid Build Coastguard Worker     if (!info) {
1575*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 0 is missing");
1576*c8dee2aaSAndroid Build Coastguard Worker         return;
1577*c8dee2aaSAndroid Build Coastguard Worker     }
1578*c8dee2aaSAndroid Build Coastguard Worker 
1579*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute pass task.
1580*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
1581*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
1582*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
1583*c8dee2aaSAndroid Build Coastguard Worker 
1584*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
1585*c8dee2aaSAndroid Build Coastguard Worker     auto buffer = sync_buffer_to_cpu(recorder.get(), info.fBuffer);
1586*c8dee2aaSAndroid Build Coastguard Worker 
1587*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
1588*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
1589*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
1590*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
1591*c8dee2aaSAndroid Build Coastguard Worker         return;
1592*c8dee2aaSAndroid Build Coastguard Worker     }
1593*c8dee2aaSAndroid Build Coastguard Worker 
1594*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
1595*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
1596*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
1597*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
1598*c8dee2aaSAndroid Build Coastguard Worker 
1599*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
1600*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kExpectedCount = kWorkgroupCount * kWorkgroupSize;
1601*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t result = static_cast<const uint32_t*>(
1602*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, buffer.get(), info.fOffset))[0];
1603*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
1604*c8dee2aaSAndroid Build Coastguard Worker                     result == kExpectedCount,
1605*c8dee2aaSAndroid Build Coastguard Worker                     "expected '%u', found '%u'",
1606*c8dee2aaSAndroid Build Coastguard Worker                     kExpectedCount,
1607*c8dee2aaSAndroid Build Coastguard Worker                     result);
1608*c8dee2aaSAndroid Build Coastguard Worker }
1609*c8dee2aaSAndroid Build Coastguard Worker 
1610*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/260622403): The shader tested here is identical to
1611*c8dee2aaSAndroid Build Coastguard Worker // `resources/sksl/compute/AtomicsOperationsOverArrayAndStruct.compute`. It would be nice to be able
1612*c8dee2aaSAndroid Build Coastguard Worker // to exercise SkSL features like this as part of SkSLTest.cpp instead of as a graphite test.
1613*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/262427430, b/262429132): Enable this test on other backends once they all support
1614*c8dee2aaSAndroid Build Coastguard Worker // compute programs.
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_AtomicOperationsOverArrayAndStructTest,reporter,context,testContext)1615*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_AtomicOperationsOverArrayAndStructTest,
1616*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
1617*c8dee2aaSAndroid Build Coastguard Worker                                               context,
1618*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
1619*c8dee2aaSAndroid Build Coastguard Worker     // This fails on Dawn D3D11, b/315834710
1620*c8dee2aaSAndroid Build Coastguard Worker     if (testContext->contextType() == skgpu::ContextType::kDawn_D3D11) {
1621*c8dee2aaSAndroid Build Coastguard Worker         return;
1622*c8dee2aaSAndroid Build Coastguard Worker     }
1623*c8dee2aaSAndroid Build Coastguard Worker 
1624*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
1625*c8dee2aaSAndroid Build Coastguard Worker 
1626*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupCount = 32;
1627*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = 128;
1628*c8dee2aaSAndroid Build Coastguard Worker 
1629*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
1630*c8dee2aaSAndroid Build Coastguard Worker     public:
1631*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
1632*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestAtomicOperationsOverArrayAndStruct",
1633*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
1634*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1635*c8dee2aaSAndroid Build Coastguard Worker                     {
1636*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
1637*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1638*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
1639*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1640*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"ssbo {\n"
1641*c8dee2aaSAndroid Build Coastguard Worker                             "   atomicUint globalCountsFirstHalf;\n"
1642*c8dee2aaSAndroid Build Coastguard Worker                             "   atomicUint globalCountsSecondHalf;\n"
1643*c8dee2aaSAndroid Build Coastguard Worker                             "}\n"
1644*c8dee2aaSAndroid Build Coastguard Worker                     }
1645*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1646*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
1647*c8dee2aaSAndroid Build Coastguard Worker 
1648*c8dee2aaSAndroid Build Coastguard Worker         // Construct a kernel that increments a two global (device memory) counters across multiple
1649*c8dee2aaSAndroid Build Coastguard Worker         // workgroups. Each workgroup maintains its own independent tallies in workgroup-shared
1650*c8dee2aaSAndroid Build Coastguard Worker         // counters which are then added to the global counts.
1651*c8dee2aaSAndroid Build Coastguard Worker         //
1652*c8dee2aaSAndroid Build Coastguard Worker         // This exercises atomic store/load/add and coherent reads and writes over memory in storage
1653*c8dee2aaSAndroid Build Coastguard Worker         // and workgroup address spaces.
1654*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1655*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1656*c8dee2aaSAndroid Build Coastguard Worker                 const uint WORKGROUP_SIZE = 128;
1657*c8dee2aaSAndroid Build Coastguard Worker 
1658*c8dee2aaSAndroid Build Coastguard Worker                 workgroup atomicUint localCounts[2];
1659*c8dee2aaSAndroid Build Coastguard Worker 
1660*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1661*c8dee2aaSAndroid Build Coastguard Worker                     // Initialize the local counts.
1662*c8dee2aaSAndroid Build Coastguard Worker                     if (sk_LocalInvocationID.x == 0) {
1663*c8dee2aaSAndroid Build Coastguard Worker                         atomicStore(localCounts[0], 0);
1664*c8dee2aaSAndroid Build Coastguard Worker                         atomicStore(localCounts[1], 0);
1665*c8dee2aaSAndroid Build Coastguard Worker                     }
1666*c8dee2aaSAndroid Build Coastguard Worker 
1667*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads in the workgroup so they all see the initial value.
1668*c8dee2aaSAndroid Build Coastguard Worker                     workgroupBarrier();
1669*c8dee2aaSAndroid Build Coastguard Worker 
1670*c8dee2aaSAndroid Build Coastguard Worker                     // Each thread increments one of the local counters based on its invocation
1671*c8dee2aaSAndroid Build Coastguard Worker                     // index.
1672*c8dee2aaSAndroid Build Coastguard Worker                     uint idx = sk_LocalInvocationID.x < (WORKGROUP_SIZE / 2) ? 0 : 1;
1673*c8dee2aaSAndroid Build Coastguard Worker                     atomicAdd(localCounts[idx], 1);
1674*c8dee2aaSAndroid Build Coastguard Worker 
1675*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads again to ensure they have all executed the increments
1676*c8dee2aaSAndroid Build Coastguard Worker                     // and the following load reads the same value across all threads in the
1677*c8dee2aaSAndroid Build Coastguard Worker                     // workgroup.
1678*c8dee2aaSAndroid Build Coastguard Worker                     workgroupBarrier();
1679*c8dee2aaSAndroid Build Coastguard Worker 
1680*c8dee2aaSAndroid Build Coastguard Worker                     // Add the workgroup-only tally to the global counter.
1681*c8dee2aaSAndroid Build Coastguard Worker                     if (sk_LocalInvocationID.x == 0) {
1682*c8dee2aaSAndroid Build Coastguard Worker                         atomicAdd(globalCountsFirstHalf, atomicLoad(localCounts[0]));
1683*c8dee2aaSAndroid Build Coastguard Worker                         atomicAdd(globalCountsSecondHalf, atomicLoad(localCounts[1]));
1684*c8dee2aaSAndroid Build Coastguard Worker                     }
1685*c8dee2aaSAndroid Build Coastguard Worker                 }
1686*c8dee2aaSAndroid Build Coastguard Worker             )";
1687*c8dee2aaSAndroid Build Coastguard Worker         }
1688*c8dee2aaSAndroid Build Coastguard Worker 
1689*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
1690*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
1691*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 0);
1692*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
1693*c8dee2aaSAndroid Build Coastguard Worker             return 2 * sizeof(uint32_t);
1694*c8dee2aaSAndroid Build Coastguard Worker         }
1695*c8dee2aaSAndroid Build Coastguard Worker 
1696*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
1697*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(kWorkgroupCount, 1, 1);
1698*c8dee2aaSAndroid Build Coastguard Worker         }
1699*c8dee2aaSAndroid Build Coastguard Worker 
1700*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
1701*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
1702*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
1703*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
1704*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 0);
1705*c8dee2aaSAndroid Build Coastguard Worker             uint32_t* data = static_cast<uint32_t*>(buffer);
1706*c8dee2aaSAndroid Build Coastguard Worker             data[0] = 0;
1707*c8dee2aaSAndroid Build Coastguard Worker             data[1] = 0;
1708*c8dee2aaSAndroid Build Coastguard Worker         }
1709*c8dee2aaSAndroid Build Coastguard Worker     } step;
1710*c8dee2aaSAndroid Build Coastguard Worker 
1711*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
1712*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step);
1713*c8dee2aaSAndroid Build Coastguard Worker 
1714*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo info = builder.getSharedBufferResource(0);
1715*c8dee2aaSAndroid Build Coastguard Worker     if (!info) {
1716*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 0 is missing");
1717*c8dee2aaSAndroid Build Coastguard Worker         return;
1718*c8dee2aaSAndroid Build Coastguard Worker     }
1719*c8dee2aaSAndroid Build Coastguard Worker 
1720*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute pass task.
1721*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
1722*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
1723*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
1724*c8dee2aaSAndroid Build Coastguard Worker 
1725*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
1726*c8dee2aaSAndroid Build Coastguard Worker     auto buffer = sync_buffer_to_cpu(recorder.get(), info.fBuffer);
1727*c8dee2aaSAndroid Build Coastguard Worker 
1728*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
1729*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
1730*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
1731*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
1732*c8dee2aaSAndroid Build Coastguard Worker         return;
1733*c8dee2aaSAndroid Build Coastguard Worker     }
1734*c8dee2aaSAndroid Build Coastguard Worker 
1735*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
1736*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
1737*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
1738*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
1739*c8dee2aaSAndroid Build Coastguard Worker 
1740*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
1741*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kExpectedCount = kWorkgroupCount * kWorkgroupSize / 2;
1742*c8dee2aaSAndroid Build Coastguard Worker 
1743*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t* ssboData = static_cast<const uint32_t*>(
1744*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, buffer.get(), info.fOffset));
1745*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t firstHalfCount = ssboData[0];
1746*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t secondHalfCount = ssboData[1];
1747*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
1748*c8dee2aaSAndroid Build Coastguard Worker                     firstHalfCount == kExpectedCount,
1749*c8dee2aaSAndroid Build Coastguard Worker                     "expected '%u', found '%u'",
1750*c8dee2aaSAndroid Build Coastguard Worker                     kExpectedCount,
1751*c8dee2aaSAndroid Build Coastguard Worker                     firstHalfCount);
1752*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
1753*c8dee2aaSAndroid Build Coastguard Worker                     secondHalfCount == kExpectedCount,
1754*c8dee2aaSAndroid Build Coastguard Worker                     "expected '%u', found '%u'",
1755*c8dee2aaSAndroid Build Coastguard Worker                     kExpectedCount,
1756*c8dee2aaSAndroid Build Coastguard Worker                     secondHalfCount);
1757*c8dee2aaSAndroid Build Coastguard Worker }
1758*c8dee2aaSAndroid Build Coastguard Worker 
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ClearedBuffer,reporter,context,testContext)1759*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ClearedBuffer,
1760*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
1761*c8dee2aaSAndroid Build Coastguard Worker                                               context,
1762*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
1763*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kProblemSize = 512;
1764*c8dee2aaSAndroid Build Coastguard Worker 
1765*c8dee2aaSAndroid Build Coastguard Worker     // The ComputeStep packs kProblemSize floats into kProblemSize / 4 vectors and each thread
1766*c8dee2aaSAndroid Build Coastguard Worker     // processes 1 vector at a time.
1767*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = kProblemSize / 4;
1768*c8dee2aaSAndroid Build Coastguard Worker 
1769*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
1770*c8dee2aaSAndroid Build Coastguard Worker 
1771*c8dee2aaSAndroid Build Coastguard Worker     // The ComputeStep requests an unmapped buffer that is zero-initialized. It writes the output to
1772*c8dee2aaSAndroid Build Coastguard Worker     // a mapped buffer which test verifies.
1773*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
1774*c8dee2aaSAndroid Build Coastguard Worker     public:
1775*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
1776*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestClearedBuffer",
1777*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
1778*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1779*c8dee2aaSAndroid Build Coastguard Worker                     // Zero initialized input buffer
1780*c8dee2aaSAndroid Build Coastguard Worker                     {
1781*c8dee2aaSAndroid Build Coastguard Worker                         // TODO(b/299979165): Declare this binding as read-only.
1782*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
1783*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kPrivate,
1784*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kClear,
1785*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"inputBlock { uint4 in_data[]; }\n",
1786*c8dee2aaSAndroid Build Coastguard Worker                     },
1787*c8dee2aaSAndroid Build Coastguard Worker                     // Output buffer:
1788*c8dee2aaSAndroid Build Coastguard Worker                     {
1789*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
1790*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,  // shared to allow us to access it from the
1791*c8dee2aaSAndroid Build Coastguard Worker                                                      // Builder
1792*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,  // mappable for read-back
1793*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1794*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock { uint4 out_data[]; }\n",
1795*c8dee2aaSAndroid Build Coastguard Worker                     }
1796*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1797*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
1798*c8dee2aaSAndroid Build Coastguard Worker 
1799*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1800*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1801*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1802*c8dee2aaSAndroid Build Coastguard Worker                     out_data[sk_GlobalInvocationID.x] = in_data[sk_GlobalInvocationID.x];
1803*c8dee2aaSAndroid Build Coastguard Worker                 }
1804*c8dee2aaSAndroid Build Coastguard Worker             )";
1805*c8dee2aaSAndroid Build Coastguard Worker         }
1806*c8dee2aaSAndroid Build Coastguard Worker 
1807*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
1808*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(uint32_t) * kProblemSize;
1809*c8dee2aaSAndroid Build Coastguard Worker         }
1810*c8dee2aaSAndroid Build Coastguard Worker 
1811*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
1812*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
1813*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
1814*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
1815*c8dee2aaSAndroid Build Coastguard Worker             // Should receive this call only for the mapped buffer.
1816*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 1);
1817*c8dee2aaSAndroid Build Coastguard Worker         }
1818*c8dee2aaSAndroid Build Coastguard Worker 
1819*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
1820*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
1821*c8dee2aaSAndroid Build Coastguard Worker         }
1822*c8dee2aaSAndroid Build Coastguard Worker     } step;
1823*c8dee2aaSAndroid Build Coastguard Worker 
1824*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
1825*c8dee2aaSAndroid Build Coastguard Worker     if (!builder.appendStep(&step)) {
1826*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to add ComputeStep to DispatchGroup");
1827*c8dee2aaSAndroid Build Coastguard Worker         return;
1828*c8dee2aaSAndroid Build Coastguard Worker     }
1829*c8dee2aaSAndroid Build Coastguard Worker 
1830*c8dee2aaSAndroid Build Coastguard Worker     // The output buffer should have been placed in the right output slot.
1831*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo outputInfo = builder.getSharedBufferResource(0);
1832*c8dee2aaSAndroid Build Coastguard Worker     if (!outputInfo) {
1833*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to allocate an output buffer at slot 0");
1834*c8dee2aaSAndroid Build Coastguard Worker         return;
1835*c8dee2aaSAndroid Build Coastguard Worker     }
1836*c8dee2aaSAndroid Build Coastguard Worker 
1837*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute task
1838*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
1839*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
1840*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
1841*c8dee2aaSAndroid Build Coastguard Worker 
1842*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
1843*c8dee2aaSAndroid Build Coastguard Worker     auto outputBuffer = sync_buffer_to_cpu(recorder.get(), outputInfo.fBuffer);
1844*c8dee2aaSAndroid Build Coastguard Worker 
1845*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
1846*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
1847*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
1848*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
1849*c8dee2aaSAndroid Build Coastguard Worker         return;
1850*c8dee2aaSAndroid Build Coastguard Worker     }
1851*c8dee2aaSAndroid Build Coastguard Worker 
1852*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
1853*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
1854*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
1855*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
1856*c8dee2aaSAndroid Build Coastguard Worker 
1857*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
1858*c8dee2aaSAndroid Build Coastguard Worker     uint32_t* outData = static_cast<uint32_t*>(
1859*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, outputBuffer.get(), outputInfo.fOffset));
1860*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(outputBuffer->isMapped() && outData != nullptr);
1861*c8dee2aaSAndroid Build Coastguard Worker     for (unsigned int i = 0; i < kProblemSize; ++i) {
1862*c8dee2aaSAndroid Build Coastguard Worker         const uint32_t found = outData[i];
1863*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, 0u == found, "expected '0u', found '%u'", found);
1864*c8dee2aaSAndroid Build Coastguard Worker     }
1865*c8dee2aaSAndroid Build Coastguard Worker }
1866*c8dee2aaSAndroid Build Coastguard Worker 
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ClearOrdering,reporter,context,testContext)1867*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ClearOrdering,
1868*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
1869*c8dee2aaSAndroid Build Coastguard Worker                                               context,
1870*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
1871*c8dee2aaSAndroid Build Coastguard Worker     // Initiate two independent DispatchGroups operating on the same buffer. The first group
1872*c8dee2aaSAndroid Build Coastguard Worker     // writes garbage to the buffer and the second group copies the contents to an output buffer.
1873*c8dee2aaSAndroid Build Coastguard Worker     // This test validates that the reads, writes, and clear occur in the expected order.
1874*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = 64;
1875*c8dee2aaSAndroid Build Coastguard Worker 
1876*c8dee2aaSAndroid Build Coastguard Worker     // Initialize buffer with non-zero data.
1877*c8dee2aaSAndroid Build Coastguard Worker     class FillWithGarbage : public ComputeStep {
1878*c8dee2aaSAndroid Build Coastguard Worker     public:
1879*c8dee2aaSAndroid Build Coastguard Worker         FillWithGarbage() : ComputeStep(
1880*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"FillWithGarbage",
1881*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
1882*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1883*c8dee2aaSAndroid Build Coastguard Worker                     {
1884*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
1885*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1886*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1887*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1888*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock { uint4 out_data[]; }\n",
1889*c8dee2aaSAndroid Build Coastguard Worker                     }
1890*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1891*c8dee2aaSAndroid Build Coastguard Worker         ~FillWithGarbage() override = default;
1892*c8dee2aaSAndroid Build Coastguard Worker 
1893*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1894*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1895*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1896*c8dee2aaSAndroid Build Coastguard Worker                     out_data[sk_GlobalInvocationID.x] = uint4(0xFE);
1897*c8dee2aaSAndroid Build Coastguard Worker                 }
1898*c8dee2aaSAndroid Build Coastguard Worker             )";
1899*c8dee2aaSAndroid Build Coastguard Worker         }
1900*c8dee2aaSAndroid Build Coastguard Worker     } garbageStep;
1901*c8dee2aaSAndroid Build Coastguard Worker 
1902*c8dee2aaSAndroid Build Coastguard Worker     // Second stage just copies the data to a destination buffer. This is only to verify that this
1903*c8dee2aaSAndroid Build Coastguard Worker     // stage, issued in a separate DispatchGroup, observes the clear.
1904*c8dee2aaSAndroid Build Coastguard Worker     class CopyBuffer : public ComputeStep {
1905*c8dee2aaSAndroid Build Coastguard Worker     public:
1906*c8dee2aaSAndroid Build Coastguard Worker         CopyBuffer() : ComputeStep(
1907*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"CopyBuffer",
1908*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
1909*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1910*c8dee2aaSAndroid Build Coastguard Worker                     {
1911*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
1912*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1913*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1914*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1915*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"inputBlock { uint4 in_data[]; }\n",
1916*c8dee2aaSAndroid Build Coastguard Worker                     },
1917*c8dee2aaSAndroid Build Coastguard Worker                     {
1918*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
1919*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1920*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1921*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/1,
1922*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock { uint4 out_data[]; }\n",
1923*c8dee2aaSAndroid Build Coastguard Worker                     }
1924*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
1925*c8dee2aaSAndroid Build Coastguard Worker         ~CopyBuffer() override = default;
1926*c8dee2aaSAndroid Build Coastguard Worker 
1927*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
1928*c8dee2aaSAndroid Build Coastguard Worker             return R"(
1929*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
1930*c8dee2aaSAndroid Build Coastguard Worker                     out_data[sk_GlobalInvocationID.x] = in_data[sk_GlobalInvocationID.x];
1931*c8dee2aaSAndroid Build Coastguard Worker                 }
1932*c8dee2aaSAndroid Build Coastguard Worker             )";
1933*c8dee2aaSAndroid Build Coastguard Worker         }
1934*c8dee2aaSAndroid Build Coastguard Worker     } copyStep;
1935*c8dee2aaSAndroid Build Coastguard Worker 
1936*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
1937*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
1938*c8dee2aaSAndroid Build Coastguard Worker 
1939*c8dee2aaSAndroid Build Coastguard Worker     constexpr size_t kElementCount = 4 * kWorkgroupSize;
1940*c8dee2aaSAndroid Build Coastguard Worker     constexpr size_t kBufferSize = sizeof(uint32_t) * kElementCount;
1941*c8dee2aaSAndroid Build Coastguard Worker     auto input = recorder->priv().drawBufferManager()->getStorage(kBufferSize);
1942*c8dee2aaSAndroid Build Coastguard Worker     auto [_, output] = recorder->priv().drawBufferManager()->getStoragePointer(kBufferSize);
1943*c8dee2aaSAndroid Build Coastguard Worker 
1944*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
1945*c8dee2aaSAndroid Build Coastguard Worker 
1946*c8dee2aaSAndroid Build Coastguard Worker     // First group.
1947*c8dee2aaSAndroid Build Coastguard Worker     builder.assignSharedBuffer(input, 0);
1948*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&garbageStep, {{1, 1, 1}});
1949*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
1950*c8dee2aaSAndroid Build Coastguard Worker 
1951*c8dee2aaSAndroid Build Coastguard Worker     // Second group.
1952*c8dee2aaSAndroid Build Coastguard Worker     builder.reset();
1953*c8dee2aaSAndroid Build Coastguard Worker     builder.assignSharedBuffer(input, 0, ClearBuffer::kYes);
1954*c8dee2aaSAndroid Build Coastguard Worker     builder.assignSharedBuffer(output, 1);
1955*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&copyStep, {{1, 1, 1}});
1956*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
1957*c8dee2aaSAndroid Build Coastguard Worker 
1958*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
1959*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
1960*c8dee2aaSAndroid Build Coastguard Worker     auto outputBuffer = sync_buffer_to_cpu(recorder.get(), output.fBuffer);
1961*c8dee2aaSAndroid Build Coastguard Worker 
1962*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
1963*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = submit_recording(context, testContext, recorder.get());
1964*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
1965*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
1966*c8dee2aaSAndroid Build Coastguard Worker         return;
1967*c8dee2aaSAndroid Build Coastguard Worker     }
1968*c8dee2aaSAndroid Build Coastguard Worker 
1969*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
1970*c8dee2aaSAndroid Build Coastguard Worker     uint32_t* outData = static_cast<uint32_t*>(
1971*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, outputBuffer.get(), output.fOffset));
1972*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(outputBuffer->isMapped() && outData != nullptr);
1973*c8dee2aaSAndroid Build Coastguard Worker     for (unsigned int i = 0; i < kElementCount; ++i) {
1974*c8dee2aaSAndroid Build Coastguard Worker         const uint32_t found = outData[i];
1975*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, 0u == found, "expected '0u', found '%u'", found);
1976*c8dee2aaSAndroid Build Coastguard Worker     }
1977*c8dee2aaSAndroid Build Coastguard Worker }
1978*c8dee2aaSAndroid Build Coastguard Worker 
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ClearOrderingScratchBuffers,reporter,context,testContext)1979*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_ClearOrderingScratchBuffers,
1980*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
1981*c8dee2aaSAndroid Build Coastguard Worker                                               context,
1982*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
1983*c8dee2aaSAndroid Build Coastguard Worker     // This test is the same as the ClearOrdering test but the two stages write to a recycled
1984*c8dee2aaSAndroid Build Coastguard Worker     // ScratchBuffer. This is primarily to test ScratchBuffer reuse.
1985*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = 64;
1986*c8dee2aaSAndroid Build Coastguard Worker 
1987*c8dee2aaSAndroid Build Coastguard Worker     // Initialize buffer with non-zero data.
1988*c8dee2aaSAndroid Build Coastguard Worker     class FillWithGarbage : public ComputeStep {
1989*c8dee2aaSAndroid Build Coastguard Worker     public:
1990*c8dee2aaSAndroid Build Coastguard Worker         FillWithGarbage() : ComputeStep(
1991*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"FillWithGarbage",
1992*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
1993*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
1994*c8dee2aaSAndroid Build Coastguard Worker                     {
1995*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
1996*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
1997*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
1998*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
1999*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock { uint4 out_data[]; }\n",
2000*c8dee2aaSAndroid Build Coastguard Worker                     }
2001*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
2002*c8dee2aaSAndroid Build Coastguard Worker         ~FillWithGarbage() override = default;
2003*c8dee2aaSAndroid Build Coastguard Worker 
2004*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
2005*c8dee2aaSAndroid Build Coastguard Worker             return R"(
2006*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
2007*c8dee2aaSAndroid Build Coastguard Worker                     out_data[sk_GlobalInvocationID.x] = uint4(0xFE);
2008*c8dee2aaSAndroid Build Coastguard Worker                 }
2009*c8dee2aaSAndroid Build Coastguard Worker             )";
2010*c8dee2aaSAndroid Build Coastguard Worker         }
2011*c8dee2aaSAndroid Build Coastguard Worker     } garbageStep;
2012*c8dee2aaSAndroid Build Coastguard Worker 
2013*c8dee2aaSAndroid Build Coastguard Worker     // Second stage just copies the data to a destination buffer. This is only to verify that this
2014*c8dee2aaSAndroid Build Coastguard Worker     // stage (issued in a separate DispatchGroup) sees the changes.
2015*c8dee2aaSAndroid Build Coastguard Worker     class CopyBuffer : public ComputeStep {
2016*c8dee2aaSAndroid Build Coastguard Worker     public:
2017*c8dee2aaSAndroid Build Coastguard Worker         CopyBuffer() : ComputeStep(
2018*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"CopyBuffer",
2019*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
2020*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
2021*c8dee2aaSAndroid Build Coastguard Worker                     {
2022*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
2023*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
2024*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
2025*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
2026*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"inputBlock { uint4 in_data[]; }\n",
2027*c8dee2aaSAndroid Build Coastguard Worker                     },
2028*c8dee2aaSAndroid Build Coastguard Worker                     {
2029*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
2030*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
2031*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kNone,
2032*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/1,
2033*c8dee2aaSAndroid Build Coastguard Worker                         /*sksl=*/"outputBlock { uint4 out_data[]; }\n",
2034*c8dee2aaSAndroid Build Coastguard Worker                     }
2035*c8dee2aaSAndroid Build Coastguard Worker                 }) {}
2036*c8dee2aaSAndroid Build Coastguard Worker         ~CopyBuffer() override = default;
2037*c8dee2aaSAndroid Build Coastguard Worker 
2038*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
2039*c8dee2aaSAndroid Build Coastguard Worker             return R"(
2040*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
2041*c8dee2aaSAndroid Build Coastguard Worker                     out_data[sk_GlobalInvocationID.x] = in_data[sk_GlobalInvocationID.x];
2042*c8dee2aaSAndroid Build Coastguard Worker                 }
2043*c8dee2aaSAndroid Build Coastguard Worker             )";
2044*c8dee2aaSAndroid Build Coastguard Worker         }
2045*c8dee2aaSAndroid Build Coastguard Worker     } copyStep;
2046*c8dee2aaSAndroid Build Coastguard Worker 
2047*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
2048*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
2049*c8dee2aaSAndroid Build Coastguard Worker 
2050*c8dee2aaSAndroid Build Coastguard Worker     constexpr size_t kElementCount = 4 * kWorkgroupSize;
2051*c8dee2aaSAndroid Build Coastguard Worker     constexpr size_t kBufferSize = sizeof(uint32_t) * kElementCount;
2052*c8dee2aaSAndroid Build Coastguard Worker     auto [_, output] = recorder->priv().drawBufferManager()->getStoragePointer(kBufferSize);
2053*c8dee2aaSAndroid Build Coastguard Worker 
2054*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
2055*c8dee2aaSAndroid Build Coastguard Worker 
2056*c8dee2aaSAndroid Build Coastguard Worker     // First group.
2057*c8dee2aaSAndroid Build Coastguard Worker     {
2058*c8dee2aaSAndroid Build Coastguard Worker         auto scratch = recorder->priv().drawBufferManager()->getScratchStorage(kBufferSize);
2059*c8dee2aaSAndroid Build Coastguard Worker         auto input = scratch.suballocate(kBufferSize);
2060*c8dee2aaSAndroid Build Coastguard Worker         builder.assignSharedBuffer(input, 0);
2061*c8dee2aaSAndroid Build Coastguard Worker 
2062*c8dee2aaSAndroid Build Coastguard Worker         // `scratch` returns to the scratch buffer pool when it goes out of scope
2063*c8dee2aaSAndroid Build Coastguard Worker     }
2064*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&garbageStep, {{1, 1, 1}});
2065*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
2066*c8dee2aaSAndroid Build Coastguard Worker 
2067*c8dee2aaSAndroid Build Coastguard Worker     // Second group.
2068*c8dee2aaSAndroid Build Coastguard Worker     builder.reset();
2069*c8dee2aaSAndroid Build Coastguard Worker     {
2070*c8dee2aaSAndroid Build Coastguard Worker         auto scratch = recorder->priv().drawBufferManager()->getScratchStorage(kBufferSize);
2071*c8dee2aaSAndroid Build Coastguard Worker         auto input = scratch.suballocate(kBufferSize);
2072*c8dee2aaSAndroid Build Coastguard Worker         builder.assignSharedBuffer(input, 0, ClearBuffer::kYes);
2073*c8dee2aaSAndroid Build Coastguard Worker     }
2074*c8dee2aaSAndroid Build Coastguard Worker     builder.assignSharedBuffer(output, 1);
2075*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&copyStep, {{1, 1, 1}});
2076*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
2077*c8dee2aaSAndroid Build Coastguard Worker 
2078*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
2079*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
2080*c8dee2aaSAndroid Build Coastguard Worker     auto outputBuffer = sync_buffer_to_cpu(recorder.get(), output.fBuffer);
2081*c8dee2aaSAndroid Build Coastguard Worker 
2082*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
2083*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = submit_recording(context, testContext, recorder.get());
2084*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
2085*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
2086*c8dee2aaSAndroid Build Coastguard Worker         return;
2087*c8dee2aaSAndroid Build Coastguard Worker     }
2088*c8dee2aaSAndroid Build Coastguard Worker 
2089*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
2090*c8dee2aaSAndroid Build Coastguard Worker     uint32_t* outData = static_cast<uint32_t*>(
2091*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, outputBuffer.get(), output.fOffset));
2092*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(outputBuffer->isMapped() && outData != nullptr);
2093*c8dee2aaSAndroid Build Coastguard Worker     for (unsigned int i = 0; i < kElementCount; ++i) {
2094*c8dee2aaSAndroid Build Coastguard Worker         const uint32_t found = outData[i];
2095*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, 0u == found, "expected '0u', found '%u'", found);
2096*c8dee2aaSAndroid Build Coastguard Worker     }
2097*c8dee2aaSAndroid Build Coastguard Worker }
2098*c8dee2aaSAndroid Build Coastguard Worker 
DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_IndirectDispatch,reporter,context,testContext)2099*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_AND_METAL_CONTEXTS(Compute_IndirectDispatch,
2100*c8dee2aaSAndroid Build Coastguard Worker                                               reporter,
2101*c8dee2aaSAndroid Build Coastguard Worker                                               context,
2102*c8dee2aaSAndroid Build Coastguard Worker                                               testContext) {
2103*c8dee2aaSAndroid Build Coastguard Worker     // This fails on Dawn D3D11, b/315834710
2104*c8dee2aaSAndroid Build Coastguard Worker     if (testContext->contextType() == skgpu::ContextType::kDawn_D3D11) {
2105*c8dee2aaSAndroid Build Coastguard Worker         return;
2106*c8dee2aaSAndroid Build Coastguard Worker     }
2107*c8dee2aaSAndroid Build Coastguard Worker 
2108*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
2109*c8dee2aaSAndroid Build Coastguard Worker 
2110*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupCount = 32;
2111*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = 64;
2112*c8dee2aaSAndroid Build Coastguard Worker 
2113*c8dee2aaSAndroid Build Coastguard Worker     // `IndirectStep` populates a buffer with the global workgroup count for `CountStep`.
2114*c8dee2aaSAndroid Build Coastguard Worker     // `CountStep` is recorded using `DispatchGroup::appendStepIndirect()` and its workgroups get
2115*c8dee2aaSAndroid Build Coastguard Worker     // dispatched according to the values computed by `IndirectStep` on the GPU.
2116*c8dee2aaSAndroid Build Coastguard Worker     class IndirectStep : public ComputeStep {
2117*c8dee2aaSAndroid Build Coastguard Worker     public:
2118*c8dee2aaSAndroid Build Coastguard Worker         IndirectStep()
2119*c8dee2aaSAndroid Build Coastguard Worker                 : ComputeStep(
2120*c8dee2aaSAndroid Build Coastguard Worker                           /*name=*/"TestIndirectDispatch_IndirectStep",
2121*c8dee2aaSAndroid Build Coastguard Worker                           /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
2122*c8dee2aaSAndroid Build Coastguard Worker                           /*resources=*/
2123*c8dee2aaSAndroid Build Coastguard Worker                           {{
2124*c8dee2aaSAndroid Build Coastguard Worker                                   /*type=*/ResourceType::kIndirectBuffer,
2125*c8dee2aaSAndroid Build Coastguard Worker                                   /*flow=*/DataFlow::kShared,
2126*c8dee2aaSAndroid Build Coastguard Worker                                   /*policy=*/ResourcePolicy::kClear,
2127*c8dee2aaSAndroid Build Coastguard Worker                                   /*slot=*/0,
2128*c8dee2aaSAndroid Build Coastguard Worker                                   // TODO(armansito): Ideally the SSBO would have a single member of
2129*c8dee2aaSAndroid Build Coastguard Worker                                   // type `IndirectDispatchArgs` struct type. SkSL modules don't
2130*c8dee2aaSAndroid Build Coastguard Worker                                   // support struct declarations so this is currently not possible.
2131*c8dee2aaSAndroid Build Coastguard Worker                                   /*sksl=*/"ssbo { uint indirect[]; }",
2132*c8dee2aaSAndroid Build Coastguard Worker                           }}) {}
2133*c8dee2aaSAndroid Build Coastguard Worker         ~IndirectStep() override = default;
2134*c8dee2aaSAndroid Build Coastguard Worker 
2135*c8dee2aaSAndroid Build Coastguard Worker         // Kernel that specifies a workgroup size of `kWorkgroupCount` to be used by the indirect
2136*c8dee2aaSAndroid Build Coastguard Worker         // dispatch.
2137*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
2138*c8dee2aaSAndroid Build Coastguard Worker             return R"(
2139*c8dee2aaSAndroid Build Coastguard Worker                 // This needs to match `kWorkgroupCount` declared above.
2140*c8dee2aaSAndroid Build Coastguard Worker                 const uint kWorkgroupCount = 32;
2141*c8dee2aaSAndroid Build Coastguard Worker 
2142*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
2143*c8dee2aaSAndroid Build Coastguard Worker                     if (sk_LocalInvocationID.x == 0) {
2144*c8dee2aaSAndroid Build Coastguard Worker                         indirect[0] = kWorkgroupCount;
2145*c8dee2aaSAndroid Build Coastguard Worker                         indirect[1] = 1;
2146*c8dee2aaSAndroid Build Coastguard Worker                         indirect[2] = 1;
2147*c8dee2aaSAndroid Build Coastguard Worker                     }
2148*c8dee2aaSAndroid Build Coastguard Worker                 }
2149*c8dee2aaSAndroid Build Coastguard Worker             )";
2150*c8dee2aaSAndroid Build Coastguard Worker         }
2151*c8dee2aaSAndroid Build Coastguard Worker 
2152*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
2153*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
2154*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 0);
2155*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
2156*c8dee2aaSAndroid Build Coastguard Worker             return kIndirectDispatchArgumentSize;
2157*c8dee2aaSAndroid Build Coastguard Worker         }
2158*c8dee2aaSAndroid Build Coastguard Worker 
2159*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
2160*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(1, 1, 1);
2161*c8dee2aaSAndroid Build Coastguard Worker         }
2162*c8dee2aaSAndroid Build Coastguard Worker     } indirectStep;
2163*c8dee2aaSAndroid Build Coastguard Worker 
2164*c8dee2aaSAndroid Build Coastguard Worker     class CountStep : public ComputeStep {
2165*c8dee2aaSAndroid Build Coastguard Worker     public:
2166*c8dee2aaSAndroid Build Coastguard Worker         CountStep()
2167*c8dee2aaSAndroid Build Coastguard Worker                 : ComputeStep(
2168*c8dee2aaSAndroid Build Coastguard Worker                           /*name=*/"TestIndirectDispatch_CountStep",
2169*c8dee2aaSAndroid Build Coastguard Worker                           /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
2170*c8dee2aaSAndroid Build Coastguard Worker                           /*resources=*/
2171*c8dee2aaSAndroid Build Coastguard Worker                           {{
2172*c8dee2aaSAndroid Build Coastguard Worker                                   /*type=*/ResourceType::kStorageBuffer,
2173*c8dee2aaSAndroid Build Coastguard Worker                                   /*flow=*/DataFlow::kShared,
2174*c8dee2aaSAndroid Build Coastguard Worker                                   /*policy=*/ResourcePolicy::kMapped,
2175*c8dee2aaSAndroid Build Coastguard Worker                                   /*slot=*/1,
2176*c8dee2aaSAndroid Build Coastguard Worker                                   /*sksl=*/"ssbo { atomicUint globalCounter; }",
2177*c8dee2aaSAndroid Build Coastguard Worker                           }}) {}
2178*c8dee2aaSAndroid Build Coastguard Worker         ~CountStep() override = default;
2179*c8dee2aaSAndroid Build Coastguard Worker 
2180*c8dee2aaSAndroid Build Coastguard Worker         std::string computeSkSL() const override {
2181*c8dee2aaSAndroid Build Coastguard Worker             return R"(
2182*c8dee2aaSAndroid Build Coastguard Worker                 workgroup atomicUint localCounter;
2183*c8dee2aaSAndroid Build Coastguard Worker 
2184*c8dee2aaSAndroid Build Coastguard Worker                 void main() {
2185*c8dee2aaSAndroid Build Coastguard Worker                     // Initialize the local counter.
2186*c8dee2aaSAndroid Build Coastguard Worker                     if (sk_LocalInvocationID.x == 0) {
2187*c8dee2aaSAndroid Build Coastguard Worker                         atomicStore(localCounter, 0);
2188*c8dee2aaSAndroid Build Coastguard Worker                     }
2189*c8dee2aaSAndroid Build Coastguard Worker 
2190*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads in the workgroup so they all see the initial value.
2191*c8dee2aaSAndroid Build Coastguard Worker                     workgroupBarrier();
2192*c8dee2aaSAndroid Build Coastguard Worker 
2193*c8dee2aaSAndroid Build Coastguard Worker                     // All threads increment the counter.
2194*c8dee2aaSAndroid Build Coastguard Worker                     atomicAdd(localCounter, 1);
2195*c8dee2aaSAndroid Build Coastguard Worker 
2196*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads again to ensure they have all executed the increment
2197*c8dee2aaSAndroid Build Coastguard Worker                     // and the following load reads the same value across all threads in the
2198*c8dee2aaSAndroid Build Coastguard Worker                     // workgroup.
2199*c8dee2aaSAndroid Build Coastguard Worker                     workgroupBarrier();
2200*c8dee2aaSAndroid Build Coastguard Worker 
2201*c8dee2aaSAndroid Build Coastguard Worker                     // Add the workgroup-only tally to the global counter.
2202*c8dee2aaSAndroid Build Coastguard Worker                     if (sk_LocalInvocationID.x == 0) {
2203*c8dee2aaSAndroid Build Coastguard Worker                         atomicAdd(globalCounter, atomicLoad(localCounter));
2204*c8dee2aaSAndroid Build Coastguard Worker                     }
2205*c8dee2aaSAndroid Build Coastguard Worker                 }
2206*c8dee2aaSAndroid Build Coastguard Worker             )";
2207*c8dee2aaSAndroid Build Coastguard Worker         }
2208*c8dee2aaSAndroid Build Coastguard Worker 
2209*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
2210*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
2211*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 1);
2212*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
2213*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(uint32_t);
2214*c8dee2aaSAndroid Build Coastguard Worker         }
2215*c8dee2aaSAndroid Build Coastguard Worker 
2216*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
2217*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
2218*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
2219*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
2220*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 0);
2221*c8dee2aaSAndroid Build Coastguard Worker             *static_cast<uint32_t*>(buffer) = 0;
2222*c8dee2aaSAndroid Build Coastguard Worker         }
2223*c8dee2aaSAndroid Build Coastguard Worker     } countStep;
2224*c8dee2aaSAndroid Build Coastguard Worker 
2225*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
2226*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&indirectStep);
2227*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo indirectBufferInfo = builder.getSharedBufferResource(0);
2228*c8dee2aaSAndroid Build Coastguard Worker     if (!indirectBufferInfo) {
2229*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Shared resource at slot 0 is missing");
2230*c8dee2aaSAndroid Build Coastguard Worker         return;
2231*c8dee2aaSAndroid Build Coastguard Worker     }
2232*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, indirectBufferInfo.fSize == kIndirectDispatchArgumentSize);
2233*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStepIndirect(&countStep, indirectBufferInfo);
2234*c8dee2aaSAndroid Build Coastguard Worker 
2235*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo info = builder.getSharedBufferResource(1);
2236*c8dee2aaSAndroid Build Coastguard Worker     if (!info) {
2237*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Shared resource at slot 1 is missing");
2238*c8dee2aaSAndroid Build Coastguard Worker         return;
2239*c8dee2aaSAndroid Build Coastguard Worker     }
2240*c8dee2aaSAndroid Build Coastguard Worker 
2241*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute pass task.
2242*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
2243*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
2244*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
2245*c8dee2aaSAndroid Build Coastguard Worker 
2246*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
2247*c8dee2aaSAndroid Build Coastguard Worker     auto buffer = sync_buffer_to_cpu(recorder.get(), info.fBuffer);
2248*c8dee2aaSAndroid Build Coastguard Worker 
2249*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
2250*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
2251*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
2252*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
2253*c8dee2aaSAndroid Build Coastguard Worker         return;
2254*c8dee2aaSAndroid Build Coastguard Worker     }
2255*c8dee2aaSAndroid Build Coastguard Worker 
2256*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
2257*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
2258*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
2259*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
2260*c8dee2aaSAndroid Build Coastguard Worker 
2261*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
2262*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kExpectedCount = kWorkgroupCount * kWorkgroupSize;
2263*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t result = static_cast<const uint32_t*>(
2264*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, buffer.get(), info.fOffset))[0];
2265*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
2266*c8dee2aaSAndroid Build Coastguard Worker                     result == kExpectedCount,
2267*c8dee2aaSAndroid Build Coastguard Worker                     "expected '%u', found '%u'",
2268*c8dee2aaSAndroid Build Coastguard Worker                     kExpectedCount,
2269*c8dee2aaSAndroid Build Coastguard Worker                     result);
2270*c8dee2aaSAndroid Build Coastguard Worker }
2271*c8dee2aaSAndroid Build Coastguard Worker 
DEF_GRAPHITE_TEST_FOR_METAL_CONTEXT(Compute_NativeShaderSourceMetal,reporter,context,testContext)2272*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_METAL_CONTEXT(Compute_NativeShaderSourceMetal,
2273*c8dee2aaSAndroid Build Coastguard Worker                                     reporter,
2274*c8dee2aaSAndroid Build Coastguard Worker                                     context,
2275*c8dee2aaSAndroid Build Coastguard Worker                                     testContext) {
2276*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
2277*c8dee2aaSAndroid Build Coastguard Worker 
2278*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupCount = 32;
2279*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = 1024;
2280*c8dee2aaSAndroid Build Coastguard Worker 
2281*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
2282*c8dee2aaSAndroid Build Coastguard Worker     public:
2283*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
2284*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestAtomicOperationsMetal",
2285*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
2286*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
2287*c8dee2aaSAndroid Build Coastguard Worker                     {
2288*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
2289*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
2290*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
2291*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
2292*c8dee2aaSAndroid Build Coastguard Worker                     }
2293*c8dee2aaSAndroid Build Coastguard Worker                 },
2294*c8dee2aaSAndroid Build Coastguard Worker                 /*workgroupBuffers=*/{},
2295*c8dee2aaSAndroid Build Coastguard Worker                 /*baseFlags=*/Flags::kSupportsNativeShader) {}
2296*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
2297*c8dee2aaSAndroid Build Coastguard Worker 
2298*c8dee2aaSAndroid Build Coastguard Worker         NativeShaderSource nativeShaderSource(NativeShaderFormat format) const override {
2299*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(format == NativeShaderFormat::kMSL);
2300*c8dee2aaSAndroid Build Coastguard Worker             static constexpr std::string_view kSource = R"(
2301*c8dee2aaSAndroid Build Coastguard Worker                 #include <metal_stdlib>
2302*c8dee2aaSAndroid Build Coastguard Worker 
2303*c8dee2aaSAndroid Build Coastguard Worker                 using namespace metal;
2304*c8dee2aaSAndroid Build Coastguard Worker 
2305*c8dee2aaSAndroid Build Coastguard Worker                 kernel void atomicCount(uint3 localId [[thread_position_in_threadgroup]],
2306*c8dee2aaSAndroid Build Coastguard Worker                                         device atomic_uint& globalCounter [[buffer(0)]]) {
2307*c8dee2aaSAndroid Build Coastguard Worker                     threadgroup atomic_uint localCounter;
2308*c8dee2aaSAndroid Build Coastguard Worker 
2309*c8dee2aaSAndroid Build Coastguard Worker                     // Initialize the local counter.
2310*c8dee2aaSAndroid Build Coastguard Worker                     if (localId.x == 0u) {
2311*c8dee2aaSAndroid Build Coastguard Worker                         atomic_store_explicit(&localCounter, 0u, memory_order_relaxed);
2312*c8dee2aaSAndroid Build Coastguard Worker                     }
2313*c8dee2aaSAndroid Build Coastguard Worker 
2314*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads in the workgroup so they all see the initial value.
2315*c8dee2aaSAndroid Build Coastguard Worker                     threadgroup_barrier(mem_flags::mem_threadgroup);
2316*c8dee2aaSAndroid Build Coastguard Worker 
2317*c8dee2aaSAndroid Build Coastguard Worker                     // All threads increment the counter.
2318*c8dee2aaSAndroid Build Coastguard Worker                     atomic_fetch_add_explicit(&localCounter, 1u, memory_order_relaxed);
2319*c8dee2aaSAndroid Build Coastguard Worker 
2320*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads again to ensure they have all executed the increment
2321*c8dee2aaSAndroid Build Coastguard Worker                     // and the following load reads the same value across all threads in the
2322*c8dee2aaSAndroid Build Coastguard Worker                     // workgroup.
2323*c8dee2aaSAndroid Build Coastguard Worker                     threadgroup_barrier(mem_flags::mem_threadgroup);
2324*c8dee2aaSAndroid Build Coastguard Worker 
2325*c8dee2aaSAndroid Build Coastguard Worker                     // Add the workgroup-only tally to the global counter.
2326*c8dee2aaSAndroid Build Coastguard Worker                     if (localId.x == 0u) {
2327*c8dee2aaSAndroid Build Coastguard Worker                         uint tally = atomic_load_explicit(&localCounter, memory_order_relaxed);
2328*c8dee2aaSAndroid Build Coastguard Worker                         atomic_fetch_add_explicit(&globalCounter, tally, memory_order_relaxed);
2329*c8dee2aaSAndroid Build Coastguard Worker                     }
2330*c8dee2aaSAndroid Build Coastguard Worker                 }
2331*c8dee2aaSAndroid Build Coastguard Worker             )";
2332*c8dee2aaSAndroid Build Coastguard Worker             return {kSource, "atomicCount"};
2333*c8dee2aaSAndroid Build Coastguard Worker         }
2334*c8dee2aaSAndroid Build Coastguard Worker 
2335*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
2336*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
2337*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 0);
2338*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
2339*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(uint32_t);
2340*c8dee2aaSAndroid Build Coastguard Worker         }
2341*c8dee2aaSAndroid Build Coastguard Worker 
2342*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
2343*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(kWorkgroupCount, 1, 1);
2344*c8dee2aaSAndroid Build Coastguard Worker         }
2345*c8dee2aaSAndroid Build Coastguard Worker 
2346*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
2347*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
2348*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
2349*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
2350*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 0);
2351*c8dee2aaSAndroid Build Coastguard Worker             *static_cast<uint32_t*>(buffer) = 0;
2352*c8dee2aaSAndroid Build Coastguard Worker         }
2353*c8dee2aaSAndroid Build Coastguard Worker     } step;
2354*c8dee2aaSAndroid Build Coastguard Worker 
2355*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
2356*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step);
2357*c8dee2aaSAndroid Build Coastguard Worker 
2358*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo info = builder.getSharedBufferResource(0);
2359*c8dee2aaSAndroid Build Coastguard Worker     if (!info) {
2360*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 0 is missing");
2361*c8dee2aaSAndroid Build Coastguard Worker         return;
2362*c8dee2aaSAndroid Build Coastguard Worker     }
2363*c8dee2aaSAndroid Build Coastguard Worker 
2364*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute pass task.
2365*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
2366*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
2367*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
2368*c8dee2aaSAndroid Build Coastguard Worker 
2369*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
2370*c8dee2aaSAndroid Build Coastguard Worker     auto buffer = sync_buffer_to_cpu(recorder.get(), info.fBuffer);
2371*c8dee2aaSAndroid Build Coastguard Worker 
2372*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
2373*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
2374*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
2375*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
2376*c8dee2aaSAndroid Build Coastguard Worker         return;
2377*c8dee2aaSAndroid Build Coastguard Worker     }
2378*c8dee2aaSAndroid Build Coastguard Worker 
2379*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
2380*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
2381*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
2382*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
2383*c8dee2aaSAndroid Build Coastguard Worker 
2384*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
2385*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kExpectedCount = kWorkgroupCount * kWorkgroupSize;
2386*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t result = static_cast<const uint32_t*>(
2387*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, buffer.get(), info.fOffset))[0];
2388*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
2389*c8dee2aaSAndroid Build Coastguard Worker                     result == kExpectedCount,
2390*c8dee2aaSAndroid Build Coastguard Worker                     "expected '%u', found '%u'",
2391*c8dee2aaSAndroid Build Coastguard Worker                     kExpectedCount,
2392*c8dee2aaSAndroid Build Coastguard Worker                     result);
2393*c8dee2aaSAndroid Build Coastguard Worker }
2394*c8dee2aaSAndroid Build Coastguard Worker 
DEF_GRAPHITE_TEST_FOR_METAL_CONTEXT(Compute_WorkgroupBufferDescMetal,reporter,context,testContext)2395*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_METAL_CONTEXT(Compute_WorkgroupBufferDescMetal,
2396*c8dee2aaSAndroid Build Coastguard Worker                                     reporter,
2397*c8dee2aaSAndroid Build Coastguard Worker                                     context,
2398*c8dee2aaSAndroid Build Coastguard Worker                                     testContext) {
2399*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
2400*c8dee2aaSAndroid Build Coastguard Worker 
2401*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupCount = 32;
2402*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = 1024;
2403*c8dee2aaSAndroid Build Coastguard Worker 
2404*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
2405*c8dee2aaSAndroid Build Coastguard Worker     public:
2406*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
2407*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestAtomicOperationsMetal",
2408*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
2409*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
2410*c8dee2aaSAndroid Build Coastguard Worker                     {
2411*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
2412*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
2413*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
2414*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
2415*c8dee2aaSAndroid Build Coastguard Worker                     }
2416*c8dee2aaSAndroid Build Coastguard Worker                 },
2417*c8dee2aaSAndroid Build Coastguard Worker                 /*workgroupBuffers=*/{
2418*c8dee2aaSAndroid Build Coastguard Worker                     {
2419*c8dee2aaSAndroid Build Coastguard Worker                         /*size=*/sizeof(uint32_t),
2420*c8dee2aaSAndroid Build Coastguard Worker                         /*index=*/0u,
2421*c8dee2aaSAndroid Build Coastguard Worker                     }
2422*c8dee2aaSAndroid Build Coastguard Worker                 },
2423*c8dee2aaSAndroid Build Coastguard Worker                 /*baseFlags=*/Flags::kSupportsNativeShader) {}
2424*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
2425*c8dee2aaSAndroid Build Coastguard Worker 
2426*c8dee2aaSAndroid Build Coastguard Worker         // This is the same MSL kernel as in Compute_NativeShaderSourceMetal, except `localCounter`
2427*c8dee2aaSAndroid Build Coastguard Worker         // is an entry-point parameter instead of a local variable. This forces the workgroup
2428*c8dee2aaSAndroid Build Coastguard Worker         // binding to be encoded explicitly in the command encoder.
2429*c8dee2aaSAndroid Build Coastguard Worker         NativeShaderSource nativeShaderSource(NativeShaderFormat format) const override {
2430*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(format == NativeShaderFormat::kMSL);
2431*c8dee2aaSAndroid Build Coastguard Worker             static constexpr std::string_view kSource = R"(
2432*c8dee2aaSAndroid Build Coastguard Worker                 #include <metal_stdlib>
2433*c8dee2aaSAndroid Build Coastguard Worker 
2434*c8dee2aaSAndroid Build Coastguard Worker                 using namespace metal;
2435*c8dee2aaSAndroid Build Coastguard Worker 
2436*c8dee2aaSAndroid Build Coastguard Worker                 kernel void atomicCount(uint3 localId [[thread_position_in_threadgroup]],
2437*c8dee2aaSAndroid Build Coastguard Worker                                         device atomic_uint& globalCounter [[buffer(0)]],
2438*c8dee2aaSAndroid Build Coastguard Worker                                         threadgroup atomic_uint& localCounter [[threadgroup(0)]]) {
2439*c8dee2aaSAndroid Build Coastguard Worker                     // Initialize the local counter.
2440*c8dee2aaSAndroid Build Coastguard Worker                     if (localId.x == 0u) {
2441*c8dee2aaSAndroid Build Coastguard Worker                         atomic_store_explicit(&localCounter, 0u, memory_order_relaxed);
2442*c8dee2aaSAndroid Build Coastguard Worker                     }
2443*c8dee2aaSAndroid Build Coastguard Worker 
2444*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads in the workgroup so they all see the initial value.
2445*c8dee2aaSAndroid Build Coastguard Worker                     threadgroup_barrier(mem_flags::mem_threadgroup);
2446*c8dee2aaSAndroid Build Coastguard Worker 
2447*c8dee2aaSAndroid Build Coastguard Worker                     // All threads increment the counter.
2448*c8dee2aaSAndroid Build Coastguard Worker                     atomic_fetch_add_explicit(&localCounter, 1u, memory_order_relaxed);
2449*c8dee2aaSAndroid Build Coastguard Worker 
2450*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads again to ensure they have all executed the increment
2451*c8dee2aaSAndroid Build Coastguard Worker                     // and the following load reads the same value across all threads in the
2452*c8dee2aaSAndroid Build Coastguard Worker                     // workgroup.
2453*c8dee2aaSAndroid Build Coastguard Worker                     threadgroup_barrier(mem_flags::mem_threadgroup);
2454*c8dee2aaSAndroid Build Coastguard Worker 
2455*c8dee2aaSAndroid Build Coastguard Worker                     // Add the workgroup-only tally to the global counter.
2456*c8dee2aaSAndroid Build Coastguard Worker                     if (localId.x == 0u) {
2457*c8dee2aaSAndroid Build Coastguard Worker                         uint tally = atomic_load_explicit(&localCounter, memory_order_relaxed);
2458*c8dee2aaSAndroid Build Coastguard Worker                         atomic_fetch_add_explicit(&globalCounter, tally, memory_order_relaxed);
2459*c8dee2aaSAndroid Build Coastguard Worker                     }
2460*c8dee2aaSAndroid Build Coastguard Worker                 }
2461*c8dee2aaSAndroid Build Coastguard Worker             )";
2462*c8dee2aaSAndroid Build Coastguard Worker             return {kSource, "atomicCount"};
2463*c8dee2aaSAndroid Build Coastguard Worker         }
2464*c8dee2aaSAndroid Build Coastguard Worker 
2465*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
2466*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
2467*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 0);
2468*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
2469*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(uint32_t);
2470*c8dee2aaSAndroid Build Coastguard Worker         }
2471*c8dee2aaSAndroid Build Coastguard Worker 
2472*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
2473*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(kWorkgroupCount, 1, 1);
2474*c8dee2aaSAndroid Build Coastguard Worker         }
2475*c8dee2aaSAndroid Build Coastguard Worker 
2476*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
2477*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
2478*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
2479*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
2480*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 0);
2481*c8dee2aaSAndroid Build Coastguard Worker             *static_cast<uint32_t*>(buffer) = 0;
2482*c8dee2aaSAndroid Build Coastguard Worker         }
2483*c8dee2aaSAndroid Build Coastguard Worker     } step;
2484*c8dee2aaSAndroid Build Coastguard Worker 
2485*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
2486*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step);
2487*c8dee2aaSAndroid Build Coastguard Worker 
2488*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo info = builder.getSharedBufferResource(0);
2489*c8dee2aaSAndroid Build Coastguard Worker     if (!info) {
2490*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 0 is missing");
2491*c8dee2aaSAndroid Build Coastguard Worker         return;
2492*c8dee2aaSAndroid Build Coastguard Worker     }
2493*c8dee2aaSAndroid Build Coastguard Worker 
2494*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute pass task.
2495*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
2496*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
2497*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
2498*c8dee2aaSAndroid Build Coastguard Worker 
2499*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
2500*c8dee2aaSAndroid Build Coastguard Worker     auto buffer = sync_buffer_to_cpu(recorder.get(), info.fBuffer);
2501*c8dee2aaSAndroid Build Coastguard Worker 
2502*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
2503*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
2504*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
2505*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
2506*c8dee2aaSAndroid Build Coastguard Worker         return;
2507*c8dee2aaSAndroid Build Coastguard Worker     }
2508*c8dee2aaSAndroid Build Coastguard Worker 
2509*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
2510*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
2511*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
2512*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
2513*c8dee2aaSAndroid Build Coastguard Worker 
2514*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
2515*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kExpectedCount = kWorkgroupCount * kWorkgroupSize;
2516*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t result = static_cast<const uint32_t*>(
2517*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, buffer.get(), info.fOffset))[0];
2518*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
2519*c8dee2aaSAndroid Build Coastguard Worker                     result == kExpectedCount,
2520*c8dee2aaSAndroid Build Coastguard Worker                     "expected '%u', found '%u'",
2521*c8dee2aaSAndroid Build Coastguard Worker                     kExpectedCount,
2522*c8dee2aaSAndroid Build Coastguard Worker                     result);
2523*c8dee2aaSAndroid Build Coastguard Worker }
2524*c8dee2aaSAndroid Build Coastguard Worker 
DEF_GRAPHITE_TEST_FOR_DAWN_CONTEXT(Compute_NativeShaderSourceWGSL,reporter,context,testContext)2525*c8dee2aaSAndroid Build Coastguard Worker DEF_GRAPHITE_TEST_FOR_DAWN_CONTEXT(Compute_NativeShaderSourceWGSL, reporter, context, testContext) {
2526*c8dee2aaSAndroid Build Coastguard Worker     // This fails on Dawn D3D11, b/315834710
2527*c8dee2aaSAndroid Build Coastguard Worker     if (testContext->contextType() == skgpu::ContextType::kDawn_D3D11) {
2528*c8dee2aaSAndroid Build Coastguard Worker         return;
2529*c8dee2aaSAndroid Build Coastguard Worker     }
2530*c8dee2aaSAndroid Build Coastguard Worker 
2531*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recorder> recorder = context->makeRecorder();
2532*c8dee2aaSAndroid Build Coastguard Worker 
2533*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupCount = 32;
2534*c8dee2aaSAndroid Build Coastguard Worker     // The WebGPU compat default workgroup size limit is 128.
2535*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kWorkgroupSize = 128;
2536*c8dee2aaSAndroid Build Coastguard Worker 
2537*c8dee2aaSAndroid Build Coastguard Worker     class TestComputeStep : public ComputeStep {
2538*c8dee2aaSAndroid Build Coastguard Worker     public:
2539*c8dee2aaSAndroid Build Coastguard Worker         TestComputeStep() : ComputeStep(
2540*c8dee2aaSAndroid Build Coastguard Worker                 /*name=*/"TestAtomicOperationsWGSL",
2541*c8dee2aaSAndroid Build Coastguard Worker                 /*localDispatchSize=*/{kWorkgroupSize, 1, 1},
2542*c8dee2aaSAndroid Build Coastguard Worker                 /*resources=*/{
2543*c8dee2aaSAndroid Build Coastguard Worker                     {
2544*c8dee2aaSAndroid Build Coastguard Worker                         /*type=*/ResourceType::kStorageBuffer,
2545*c8dee2aaSAndroid Build Coastguard Worker                         /*flow=*/DataFlow::kShared,
2546*c8dee2aaSAndroid Build Coastguard Worker                         /*policy=*/ResourcePolicy::kMapped,
2547*c8dee2aaSAndroid Build Coastguard Worker                         /*slot=*/0,
2548*c8dee2aaSAndroid Build Coastguard Worker                     }
2549*c8dee2aaSAndroid Build Coastguard Worker                 },
2550*c8dee2aaSAndroid Build Coastguard Worker                 /*workgroupBuffers=*/{},
2551*c8dee2aaSAndroid Build Coastguard Worker                 /*baseFlags=*/Flags::kSupportsNativeShader) {}
2552*c8dee2aaSAndroid Build Coastguard Worker         ~TestComputeStep() override = default;
2553*c8dee2aaSAndroid Build Coastguard Worker 
2554*c8dee2aaSAndroid Build Coastguard Worker         NativeShaderSource nativeShaderSource(NativeShaderFormat format) const override {
2555*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(format == NativeShaderFormat::kWGSL);
2556*c8dee2aaSAndroid Build Coastguard Worker             static constexpr std::string_view kSource = R"(
2557*c8dee2aaSAndroid Build Coastguard Worker                 @group(0) @binding(0) var<storage, read_write> globalCounter: atomic<u32>;
2558*c8dee2aaSAndroid Build Coastguard Worker 
2559*c8dee2aaSAndroid Build Coastguard Worker                 var<workgroup> localCounter: atomic<u32>;
2560*c8dee2aaSAndroid Build Coastguard Worker 
2561*c8dee2aaSAndroid Build Coastguard Worker                 @compute @workgroup_size(128)
2562*c8dee2aaSAndroid Build Coastguard Worker                 fn atomicCount(@builtin(local_invocation_id) localId: vec3u) {
2563*c8dee2aaSAndroid Build Coastguard Worker                     // Initialize the local counter.
2564*c8dee2aaSAndroid Build Coastguard Worker                     if localId.x == 0u {
2565*c8dee2aaSAndroid Build Coastguard Worker                         atomicStore(&localCounter, 0u);
2566*c8dee2aaSAndroid Build Coastguard Worker                     }
2567*c8dee2aaSAndroid Build Coastguard Worker 
2568*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads in the workgroup so they all see the initial value.
2569*c8dee2aaSAndroid Build Coastguard Worker                     workgroupBarrier();
2570*c8dee2aaSAndroid Build Coastguard Worker 
2571*c8dee2aaSAndroid Build Coastguard Worker                     // All threads increment the counter.
2572*c8dee2aaSAndroid Build Coastguard Worker                     atomicAdd(&localCounter, 1u);
2573*c8dee2aaSAndroid Build Coastguard Worker 
2574*c8dee2aaSAndroid Build Coastguard Worker                     // Synchronize the threads again to ensure they have all executed the increment
2575*c8dee2aaSAndroid Build Coastguard Worker                     // and the following load reads the same value across all threads in the
2576*c8dee2aaSAndroid Build Coastguard Worker                     // workgroup.
2577*c8dee2aaSAndroid Build Coastguard Worker                     workgroupBarrier();
2578*c8dee2aaSAndroid Build Coastguard Worker 
2579*c8dee2aaSAndroid Build Coastguard Worker                     // Add the workgroup-only tally to the global counter.
2580*c8dee2aaSAndroid Build Coastguard Worker                     if localId.x == 0u {
2581*c8dee2aaSAndroid Build Coastguard Worker                         let tally = atomicLoad(&localCounter);
2582*c8dee2aaSAndroid Build Coastguard Worker                         atomicAdd(&globalCounter, tally);
2583*c8dee2aaSAndroid Build Coastguard Worker                     }
2584*c8dee2aaSAndroid Build Coastguard Worker                 }
2585*c8dee2aaSAndroid Build Coastguard Worker             )";
2586*c8dee2aaSAndroid Build Coastguard Worker             return {kSource, "atomicCount"};
2587*c8dee2aaSAndroid Build Coastguard Worker         }
2588*c8dee2aaSAndroid Build Coastguard Worker 
2589*c8dee2aaSAndroid Build Coastguard Worker         size_t calculateBufferSize(int index, const ResourceDesc& r) const override {
2590*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(index == 0);
2591*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fSlot == 0);
2592*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(r.fFlow == DataFlow::kShared);
2593*c8dee2aaSAndroid Build Coastguard Worker             return sizeof(uint32_t);
2594*c8dee2aaSAndroid Build Coastguard Worker         }
2595*c8dee2aaSAndroid Build Coastguard Worker 
2596*c8dee2aaSAndroid Build Coastguard Worker         WorkgroupSize calculateGlobalDispatchSize() const override {
2597*c8dee2aaSAndroid Build Coastguard Worker             return WorkgroupSize(kWorkgroupCount, 1, 1);
2598*c8dee2aaSAndroid Build Coastguard Worker         }
2599*c8dee2aaSAndroid Build Coastguard Worker 
2600*c8dee2aaSAndroid Build Coastguard Worker         void prepareStorageBuffer(int resourceIndex,
2601*c8dee2aaSAndroid Build Coastguard Worker                                   const ResourceDesc& r,
2602*c8dee2aaSAndroid Build Coastguard Worker                                   void* buffer,
2603*c8dee2aaSAndroid Build Coastguard Worker                                   size_t bufferSize) const override {
2604*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resourceIndex == 0);
2605*c8dee2aaSAndroid Build Coastguard Worker             *static_cast<uint32_t*>(buffer) = 0;
2606*c8dee2aaSAndroid Build Coastguard Worker         }
2607*c8dee2aaSAndroid Build Coastguard Worker     } step;
2608*c8dee2aaSAndroid Build Coastguard Worker 
2609*c8dee2aaSAndroid Build Coastguard Worker     DispatchGroup::Builder builder(recorder.get());
2610*c8dee2aaSAndroid Build Coastguard Worker     builder.appendStep(&step);
2611*c8dee2aaSAndroid Build Coastguard Worker 
2612*c8dee2aaSAndroid Build Coastguard Worker     BindBufferInfo info = builder.getSharedBufferResource(0);
2613*c8dee2aaSAndroid Build Coastguard Worker     if (!info) {
2614*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "shared resource at slot 0 is missing");
2615*c8dee2aaSAndroid Build Coastguard Worker         return;
2616*c8dee2aaSAndroid Build Coastguard Worker     }
2617*c8dee2aaSAndroid Build Coastguard Worker 
2618*c8dee2aaSAndroid Build Coastguard Worker     // Record the compute pass task.
2619*c8dee2aaSAndroid Build Coastguard Worker     ComputeTask::DispatchGroupList groups;
2620*c8dee2aaSAndroid Build Coastguard Worker     groups.push_back(builder.finalize());
2621*c8dee2aaSAndroid Build Coastguard Worker     recorder->priv().add(ComputeTask::Make(std::move(groups)));
2622*c8dee2aaSAndroid Build Coastguard Worker 
2623*c8dee2aaSAndroid Build Coastguard Worker     // Ensure the output buffer is synchronized to the CPU once the GPU submission has finished.
2624*c8dee2aaSAndroid Build Coastguard Worker     auto buffer = sync_buffer_to_cpu(recorder.get(), info.fBuffer);
2625*c8dee2aaSAndroid Build Coastguard Worker 
2626*c8dee2aaSAndroid Build Coastguard Worker     // Submit the work and wait for it to complete.
2627*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Recording> recording = recorder->snap();
2628*c8dee2aaSAndroid Build Coastguard Worker     if (!recording) {
2629*c8dee2aaSAndroid Build Coastguard Worker         ERRORF(reporter, "Failed to make recording");
2630*c8dee2aaSAndroid Build Coastguard Worker         return;
2631*c8dee2aaSAndroid Build Coastguard Worker     }
2632*c8dee2aaSAndroid Build Coastguard Worker 
2633*c8dee2aaSAndroid Build Coastguard Worker     InsertRecordingInfo insertInfo;
2634*c8dee2aaSAndroid Build Coastguard Worker     insertInfo.fRecording = recording.get();
2635*c8dee2aaSAndroid Build Coastguard Worker     context->insertRecording(insertInfo);
2636*c8dee2aaSAndroid Build Coastguard Worker     testContext->syncedSubmit(context);
2637*c8dee2aaSAndroid Build Coastguard Worker 
2638*c8dee2aaSAndroid Build Coastguard Worker     // Verify the contents of the output buffer.
2639*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t kExpectedCount = kWorkgroupCount * kWorkgroupSize;
2640*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t result = static_cast<const uint32_t*>(
2641*c8dee2aaSAndroid Build Coastguard Worker             map_buffer(context, testContext, buffer.get(), info.fOffset))[0];
2642*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
2643*c8dee2aaSAndroid Build Coastguard Worker                     result == kExpectedCount,
2644*c8dee2aaSAndroid Build Coastguard Worker                     "expected '%u', found '%u'",
2645*c8dee2aaSAndroid Build Coastguard Worker                     kExpectedCount,
2646*c8dee2aaSAndroid Build Coastguard Worker                     result);
2647*c8dee2aaSAndroid Build Coastguard Worker }
2648