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(©Step, {{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(©Step, {{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