Flutter Impeller
compute_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/fml/synchronization/waitable_event.h"
6 #include "flutter/testing/testing.h"
7 #include "gmock/gmock.h"
9 #include "impeller/fixtures/sample.comp.h"
10 #include "impeller/fixtures/stage1.comp.h"
11 #include "impeller/fixtures/stage2.comp.h"
14 #include "impeller/renderer/compute_3d_test.comp.h"
17 #include "impeller/renderer/prefix_sum_test.comp.h"
18 #include "impeller/renderer/threadgroup_sizing_test.comp.h"
19 
20 namespace {
21 std::shared_ptr<impeller::HostBuffer> CreateHostBufferFromContext(
22  const std::shared_ptr<impeller::Context>& context) {
24  context->GetResourceAllocator(), context->GetIdleWaiter(),
25  context->GetCapabilities()->GetMinimumUniformAlignment());
26 }
27 
28 // The number of workgroups needed to cover `invocations` invocations given a
29 // per-workgroup `local_size`. A `local_size` of 0 means the shader sizes its
30 // workgroup with a specialization constant, in which case the caller should
31 // dispatch an explicit count rather than derive one here.
32 constexpr uint32_t WorkgroupCount(size_t invocations, uint32_t local_size) {
33  if (local_size == 0u) {
34  return 0u;
35  }
36  return static_cast<uint32_t>((invocations + local_size - 1) / local_size);
37 }
38 } // namespace
39 
40 namespace impeller {
41 namespace testing {
44 
45 TEST_P(ComputeTest, CapabilitiesReportSupport) {
46  auto context = GetContext();
47  ASSERT_TRUE(context);
48  ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
49 }
50 
51 TEST_P(ComputeTest, CanCreateComputePass) {
52  using CS = SampleComputeShader;
53  auto context = GetContext();
54  auto host_buffer = CreateHostBufferFromContext(context);
55  ASSERT_TRUE(context);
56  ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
57 
58  using SamplePipelineBuilder = ComputePipelineBuilder<CS>;
59  auto pipeline_desc =
60  SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
61  ASSERT_TRUE(pipeline_desc.has_value());
62  auto compute_pipeline =
63  context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
64  ASSERT_TRUE(compute_pipeline);
65 
66  auto cmd_buffer = context->CreateCommandBuffer();
67  auto pass = cmd_buffer->CreateComputePass();
68  ASSERT_TRUE(pass && pass->IsValid());
69 
70  static constexpr size_t kCount = 5;
71 
72  pass->SetPipeline(compute_pipeline);
73 
74  CS::Info info{.count = kCount};
75  CS::Input0<kCount> input_0;
76  CS::Input1<kCount> input_1;
77  for (size_t i = 0; i < kCount; i++) {
78  input_0.elements[i] = Vector4(2.0 + i, 3.0 + i, 4.0 + i, 5.0 * i);
79  input_1.elements[i] = Vector4(6.0, 7.0, 8.0, 9.0);
80  }
81 
82  input_0.fixed_array[1] = IPoint32(2, 2);
83  input_1.fixed_array[0] = UintPoint32(3, 3);
84  input_0.some_int = 5;
85  input_1.some_struct = CS::SomeStruct{.vf = Point(3, 4), .i = 42};
86 
87  auto output_buffer = CreateHostVisibleDeviceBuffer<CS::Output<kCount>>(
88  context, "Output Buffer");
89 
90  CS::BindInfo(*pass, host_buffer->EmplaceUniform(info));
91  CS::BindInput0(*pass, host_buffer->EmplaceStorageBuffer(input_0));
92  CS::BindInput1(*pass, host_buffer->EmplaceStorageBuffer(input_1));
93  CS::BindOutput(*pass, DeviceBuffer::AsBufferView(output_buffer));
94 
95  ASSERT_TRUE(
96  pass->Compute({WorkgroupCount(kCount, CS::kWorkgroupSize[0]), 1, 1})
97  .ok());
98  ASSERT_TRUE(pass->EncodeCommands());
99 
100  fml::AutoResetWaitableEvent latch;
101  ASSERT_TRUE(
102  context->GetCommandQueue()
103  ->Submit(
104  {cmd_buffer},
105  [&latch, output_buffer, &input_0,
106  &input_1](CommandBuffer::Status status) {
107  EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
108 
109  auto view = DeviceBuffer::AsBufferView(output_buffer);
110  EXPECT_EQ(view.GetRange().length, sizeof(CS::Output<kCount>));
111 
112  CS::Output<kCount>* output =
113  reinterpret_cast<CS::Output<kCount>*>(
114  output_buffer->OnGetContents());
115  EXPECT_TRUE(output);
116  for (size_t i = 0; i < kCount; i++) {
117  Vector4 vector = output->elements[i];
118  Vector4 computed = input_0.elements[i] * input_1.elements[i];
119  EXPECT_EQ(vector,
120  Vector4(computed.x + 2 + input_1.some_struct.i,
121  computed.y + 3 + input_1.some_struct.vf.x,
122  computed.z + 5 + input_1.some_struct.vf.y,
123  computed.w));
124  }
125  latch.Signal();
126  })
127  .ok());
128 
129  latch.Wait();
130 }
131 
132 TEST_P(ComputeTest, CanComputePrefixSum) {
133  using CS = PrefixSumTestComputeShader;
134  auto context = GetContext();
135  auto host_buffer = CreateHostBufferFromContext(context);
136  ASSERT_TRUE(context);
137  ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
138 
139  using SamplePipelineBuilder = ComputePipelineBuilder<CS>;
140  auto pipeline_desc =
141  SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
142  ASSERT_TRUE(pipeline_desc.has_value());
143  auto compute_pipeline =
144  context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
145  ASSERT_TRUE(compute_pipeline);
146 
147  auto cmd_buffer = context->CreateCommandBuffer();
148  auto pass = cmd_buffer->CreateComputePass();
149  ASSERT_TRUE(pass && pass->IsValid());
150 
151  static constexpr size_t kCount = 5;
152 
153  pass->SetPipeline(compute_pipeline);
154 
155  CS::InputData<kCount> input_data;
156  input_data.count = kCount;
157  for (size_t i = 0; i < kCount; i++) {
158  input_data.data[i] = 1 + i;
159  }
160 
161  auto output_buffer = CreateHostVisibleDeviceBuffer<CS::OutputData<kCount>>(
162  context, "Output Buffer");
163 
164  CS::BindInputData(*pass, host_buffer->EmplaceStorageBuffer(input_data));
165  CS::BindOutputData(*pass, DeviceBuffer::AsBufferView(output_buffer));
166 
167  // The prefix sum is computed within a single workgroup whose size is sized to
168  // the device (a specialization constant), so dispatch exactly one.
169  ASSERT_TRUE(pass->Compute({1, 1, 1}).ok());
170  ASSERT_TRUE(pass->EncodeCommands());
171 
172  fml::AutoResetWaitableEvent latch;
173  ASSERT_TRUE(
174  context->GetCommandQueue()
175  ->Submit({cmd_buffer},
176  [&latch, output_buffer](CommandBuffer::Status status) {
177  EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
178 
179  auto view = DeviceBuffer::AsBufferView(output_buffer);
180  EXPECT_EQ(view.GetRange().length,
181  sizeof(CS::OutputData<kCount>));
182 
183  CS::OutputData<kCount>* output =
184  reinterpret_cast<CS::OutputData<kCount>*>(
185  output_buffer->OnGetContents());
186  EXPECT_TRUE(output);
187 
188  constexpr uint32_t expected[kCount] = {1, 3, 6, 10, 15};
189  for (size_t i = 0; i < kCount; i++) {
190  auto computed_sum = output->data[i];
191  EXPECT_EQ(computed_sum, expected[i]);
192  }
193  latch.Signal();
194  })
195  .ok());
196 
197  latch.Wait();
198 }
199 
200 TEST_P(ComputeTest, 1DThreadgroupSizingIsCorrect) {
201  using CS = ThreadgroupSizingTestComputeShader;
202  auto context = GetContext();
203  ASSERT_TRUE(context);
204  ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
205 
206  using SamplePipelineBuilder = ComputePipelineBuilder<CS>;
207  auto pipeline_desc =
208  SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
209  ASSERT_TRUE(pipeline_desc.has_value());
210  auto compute_pipeline =
211  context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
212  ASSERT_TRUE(compute_pipeline);
213 
214  auto cmd_buffer = context->CreateCommandBuffer();
215  auto pass = cmd_buffer->CreateComputePass();
216  ASSERT_TRUE(pass && pass->IsValid());
217 
218  static constexpr size_t kCount = 2048;
219 
220  pass->SetPipeline(compute_pipeline);
221 
222  auto output_buffer = CreateHostVisibleDeviceBuffer<CS::OutputData<kCount>>(
223  context, "Output Buffer");
224 
225  CS::BindOutputData(*pass, DeviceBuffer::AsBufferView(output_buffer));
226 
227  ASSERT_TRUE(
228  pass->Compute({WorkgroupCount(kCount, CS::kWorkgroupSize[0]), 1, 1})
229  .ok());
230  ASSERT_TRUE(pass->EncodeCommands());
231 
232  fml::AutoResetWaitableEvent latch;
233  ASSERT_TRUE(
234  context->GetCommandQueue()
235  ->Submit({cmd_buffer},
236  [&latch, output_buffer](CommandBuffer::Status status) {
237  EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
238 
239  auto view = DeviceBuffer::AsBufferView(output_buffer);
240  EXPECT_EQ(view.GetRange().length,
241  sizeof(CS::OutputData<kCount>));
242 
243  CS::OutputData<kCount>* output =
244  reinterpret_cast<CS::OutputData<kCount>*>(
245  output_buffer->OnGetContents());
246  EXPECT_TRUE(output);
247  EXPECT_EQ(output->data[kCount - 1], kCount - 1);
248  latch.Signal();
249  })
250  .ok());
251 
252  latch.Wait();
253 }
254 
255 TEST_P(ComputeTest, 3DWorkgroupDispatchIsCorrect) {
256  using CS = Compute3dTestComputeShader;
257  auto context = GetContext();
258  ASSERT_TRUE(context);
259  ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
260 
262  auto pipeline_desc = PipelineBuilder::MakeDefaultPipelineDescriptor(*context);
263  ASSERT_TRUE(pipeline_desc.has_value());
264  auto compute_pipeline =
265  context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
266  ASSERT_TRUE(compute_pipeline);
267 
268  auto cmd_buffer = context->CreateCommandBuffer();
269  auto pass = cmd_buffer->CreateComputePass();
270  ASSERT_TRUE(pass && pass->IsValid());
271 
272  // The shader's local size is (2, 3, 4). Dispatching (3, 2, 1) workgroups
273  // covers a (6, 6, 4) invocation grid, which exercises all three dimensions
274  // and confirms the reflected local size is honored on every axis.
275  static_assert(CS::kWorkgroupSize[0] == 2 && CS::kWorkgroupSize[1] == 3 &&
276  CS::kWorkgroupSize[2] == 4);
277  constexpr std::array<uint32_t, 3> kWorkgroups = {3, 2, 1};
278  constexpr uint32_t kWidth = kWorkgroups[0] * CS::kWorkgroupSize[0];
279  constexpr uint32_t kHeight = kWorkgroups[1] * CS::kWorkgroupSize[1];
280  constexpr uint32_t kDepth = kWorkgroups[2] * CS::kWorkgroupSize[2];
281  constexpr size_t kCount = kWidth * kHeight * kDepth;
282 
283  pass->SetPipeline(compute_pipeline);
284 
285  auto output_buffer = CreateHostVisibleDeviceBuffer<CS::OutputData<kCount>>(
286  context, "Output Buffer");
287  CS::BindOutputData(*pass, DeviceBuffer::AsBufferView(output_buffer));
288 
289  ASSERT_TRUE(pass->Compute(kWorkgroups).ok());
290  ASSERT_TRUE(pass->EncodeCommands());
291 
292  fml::AutoResetWaitableEvent latch;
293  ASSERT_TRUE(
294  context->GetCommandQueue()
295  ->Submit({cmd_buffer},
296  [&latch, output_buffer](CommandBuffer::Status status) {
297  EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
298 
299  CS::OutputData<kCount>* output =
300  reinterpret_cast<CS::OutputData<kCount>*>(
301  output_buffer->OnGetContents());
302  EXPECT_TRUE(output);
303  for (uint32_t i = 0; i < kCount; i++) {
304  EXPECT_EQ(output->data[i], i);
305  }
306  latch.Signal();
307  })
308  .ok());
309 
310  latch.Wait();
311 }
312 
313 TEST_P(ComputeTest, CanComputePrefixSumLargeInteractive) {
314  using CS = PrefixSumTestComputeShader;
315 
316  auto context = GetContext();
317  auto host_buffer = CreateHostBufferFromContext(context);
318 
319  ASSERT_TRUE(context);
320  ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
321 
322  auto callback = [&](RenderPass& render_pass) -> bool {
323  using SamplePipelineBuilder = ComputePipelineBuilder<CS>;
324  auto pipeline_desc =
325  SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
326  auto compute_pipeline =
327  context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
328 
329  auto cmd_buffer = context->CreateCommandBuffer();
330  auto pass = cmd_buffer->CreateComputePass();
331 
332  static constexpr size_t kCount = 1023;
333 
334  pass->SetPipeline(compute_pipeline);
335 
336  CS::InputData<kCount> input_data;
337  input_data.count = kCount;
338  for (size_t i = 0; i < kCount; i++) {
339  input_data.data[i] = 1 + i;
340  }
341 
342  auto output_buffer = CreateHostVisibleDeviceBuffer<CS::OutputData<kCount>>(
343  context, "Output Buffer");
344 
345  CS::BindInputData(*pass, host_buffer->EmplaceStorageBuffer(input_data));
346  CS::BindOutputData(*pass, DeviceBuffer::AsBufferView(output_buffer));
347 
348  // Single workgroup; see CanComputePrefixSum.
349  pass->Compute({1, 1, 1});
350  pass->EncodeCommands();
351  host_buffer->Reset();
352  return context->GetCommandQueue()->Submit({cmd_buffer}).ok();
353  };
354  ASSERT_TRUE(OpenPlaygroundHere(callback));
355 }
356 
357 TEST_P(ComputeTest, MultiStageInputAndOutput) {
358  using CS1 = Stage1ComputeShader;
359  using Stage1PipelineBuilder = ComputePipelineBuilder<CS1>;
360  using CS2 = Stage2ComputeShader;
361  using Stage2PipelineBuilder = ComputePipelineBuilder<CS2>;
362 
363  auto context = GetContext();
364  auto host_buffer = CreateHostBufferFromContext(context);
365  ASSERT_TRUE(context);
366  ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
367 
368  auto pipeline_desc_1 =
369  Stage1PipelineBuilder::MakeDefaultPipelineDescriptor(*context);
370  ASSERT_TRUE(pipeline_desc_1.has_value());
371  auto compute_pipeline_1 =
372  context->GetPipelineLibrary()->GetPipeline(pipeline_desc_1).Get();
373  ASSERT_TRUE(compute_pipeline_1);
374 
375  auto pipeline_desc_2 =
376  Stage2PipelineBuilder::MakeDefaultPipelineDescriptor(*context);
377  ASSERT_TRUE(pipeline_desc_2.has_value());
378  auto compute_pipeline_2 =
379  context->GetPipelineLibrary()->GetPipeline(pipeline_desc_2).Get();
380  ASSERT_TRUE(compute_pipeline_2);
381 
382  auto cmd_buffer = context->CreateCommandBuffer();
383  auto pass = cmd_buffer->CreateComputePass();
384  ASSERT_TRUE(pass && pass->IsValid());
385 
386  static constexpr size_t kCount1 = 5;
387  static constexpr size_t kCount2 = kCount1 * 2;
388 
389  CS1::Input<kCount1> input_1;
390  input_1.count = kCount1;
391  for (size_t i = 0; i < kCount1; i++) {
392  input_1.elements[i] = i;
393  }
394 
395  CS2::Input<kCount2> input_2;
396  input_2.count = kCount2;
397  for (size_t i = 0; i < kCount2; i++) {
398  input_2.elements[i] = i;
399  }
400 
401  auto output_buffer_1 = CreateHostVisibleDeviceBuffer<CS1::Output<kCount2>>(
402  context, "Output Buffer Stage 1");
403  auto output_buffer_2 = CreateHostVisibleDeviceBuffer<CS2::Output<kCount2>>(
404  context, "Output Buffer Stage 2");
405 
406  {
407  pass->SetPipeline(compute_pipeline_1);
408 
409  CS1::BindInput(*pass, host_buffer->EmplaceStorageBuffer(input_1));
410  CS1::BindOutput(*pass, DeviceBuffer::AsBufferView(output_buffer_1));
411 
412  ASSERT_TRUE(
413  pass->Compute({WorkgroupCount(kCount1, CS1::kWorkgroupSize[0]), 1, 1})
414  .ok());
415  pass->AddBufferMemoryBarrier();
416  }
417 
418  {
419  pass->SetPipeline(compute_pipeline_2);
420 
421  CS1::BindInput(*pass, DeviceBuffer::AsBufferView(output_buffer_1));
422  CS2::BindOutput(*pass, DeviceBuffer::AsBufferView(output_buffer_2));
423  ASSERT_TRUE(
424  pass->Compute({WorkgroupCount(kCount2, CS2::kWorkgroupSize[0]), 1, 1})
425  .ok());
426  }
427 
428  ASSERT_TRUE(pass->EncodeCommands());
429 
430  fml::AutoResetWaitableEvent latch;
431  ASSERT_TRUE(
432  context->GetCommandQueue()
433  ->Submit({cmd_buffer},
434  [&latch, &output_buffer_1,
435  &output_buffer_2](CommandBuffer::Status status) {
436  EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
437 
438  CS1::Output<kCount2>* output_1 =
439  reinterpret_cast<CS1::Output<kCount2>*>(
440  output_buffer_1->OnGetContents());
441  EXPECT_TRUE(output_1);
442  EXPECT_EQ(output_1->count, 10u);
443  EXPECT_THAT(
444  output_1->elements,
445  ::testing::ElementsAre(0, 0, 2, 3, 4, 6, 6, 9, 8, 12));
446 
447  CS2::Output<kCount2>* output_2 =
448  reinterpret_cast<CS2::Output<kCount2>*>(
449  output_buffer_2->OnGetContents());
450  EXPECT_TRUE(output_2);
451  EXPECT_EQ(output_2->count, 10u);
452  EXPECT_THAT(output_2->elements,
453  ::testing::ElementsAre(0, 0, 4, 6, 8, 12, 12,
454  18, 16, 24));
455 
456  latch.Signal();
457  })
458  .ok());
459 
460  latch.Wait();
461 }
462 
463 TEST_P(ComputeTest, CanCompute1DimensionalData) {
464  using CS = SampleComputeShader;
465  auto context = GetContext();
466  auto host_buffer = CreateHostBufferFromContext(context);
467  ASSERT_TRUE(context);
468  ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
469 
470  using SamplePipelineBuilder = ComputePipelineBuilder<CS>;
471  auto pipeline_desc =
472  SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
473  ASSERT_TRUE(pipeline_desc.has_value());
474  auto compute_pipeline =
475  context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
476  ASSERT_TRUE(compute_pipeline);
477 
478  auto cmd_buffer = context->CreateCommandBuffer();
479  auto pass = cmd_buffer->CreateComputePass();
480  ASSERT_TRUE(pass && pass->IsValid());
481 
482  static constexpr size_t kCount = 5;
483 
484  pass->SetPipeline(compute_pipeline);
485 
486  CS::Info info{.count = kCount};
487  CS::Input0<kCount> input_0;
488  CS::Input1<kCount> input_1;
489  for (size_t i = 0; i < kCount; i++) {
490  input_0.elements[i] = Vector4(2.0 + i, 3.0 + i, 4.0 + i, 5.0 * i);
491  input_1.elements[i] = Vector4(6.0, 7.0, 8.0, 9.0);
492  }
493 
494  input_0.fixed_array[1] = IPoint32(2, 2);
495  input_1.fixed_array[0] = UintPoint32(3, 3);
496  input_0.some_int = 5;
497  input_1.some_struct = CS::SomeStruct{.vf = Point(3, 4), .i = 42};
498 
499  auto output_buffer = CreateHostVisibleDeviceBuffer<CS::Output<kCount>>(
500  context, "Output Buffer");
501 
502  CS::BindInfo(*pass, host_buffer->EmplaceUniform(info));
503  CS::BindInput0(*pass, host_buffer->EmplaceStorageBuffer(input_0));
504  CS::BindInput1(*pass, host_buffer->EmplaceStorageBuffer(input_1));
505  CS::BindOutput(*pass, DeviceBuffer::AsBufferView(output_buffer));
506 
507  ASSERT_TRUE(
508  pass->Compute({WorkgroupCount(kCount, CS::kWorkgroupSize[0]), 1, 1})
509  .ok());
510  ASSERT_TRUE(pass->EncodeCommands());
511 
512  fml::AutoResetWaitableEvent latch;
513  ASSERT_TRUE(
514  context->GetCommandQueue()
515  ->Submit(
516  {cmd_buffer},
517  [&latch, output_buffer, &input_0,
518  &input_1](CommandBuffer::Status status) {
519  EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
520 
521  auto view = DeviceBuffer::AsBufferView(output_buffer);
522  EXPECT_EQ(view.GetRange().length, sizeof(CS::Output<kCount>));
523 
524  CS::Output<kCount>* output =
525  reinterpret_cast<CS::Output<kCount>*>(
526  output_buffer->OnGetContents());
527  EXPECT_TRUE(output);
528  for (size_t i = 0; i < kCount; i++) {
529  Vector4 vector = output->elements[i];
530  Vector4 computed = input_0.elements[i] * input_1.elements[i];
531  EXPECT_EQ(vector,
532  Vector4(computed.x + 2 + input_1.some_struct.i,
533  computed.y + 3 + input_1.some_struct.vf.x,
534  computed.z + 5 + input_1.some_struct.vf.y,
535  computed.w));
536  }
537  latch.Signal();
538  })
539  .ok());
540 
541  latch.Wait();
542 }
543 
544 TEST_P(ComputeTest, ReturnsEarlyWhenAnyGridDimensionIsZero) {
545  using CS = SampleComputeShader;
546  auto context = GetContext();
547  auto host_buffer = CreateHostBufferFromContext(context);
548  ASSERT_TRUE(context);
549  ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
550 
551  using SamplePipelineBuilder = ComputePipelineBuilder<CS>;
552  auto pipeline_desc =
553  SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
554  ASSERT_TRUE(pipeline_desc.has_value());
555  auto compute_pipeline =
556  context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
557  ASSERT_TRUE(compute_pipeline);
558 
559  auto cmd_buffer = context->CreateCommandBuffer();
560  auto pass = cmd_buffer->CreateComputePass();
561  ASSERT_TRUE(pass && pass->IsValid());
562 
563  static constexpr size_t kCount = 5;
564 
565  pass->SetPipeline(compute_pipeline);
566 
567  CS::Info info{.count = kCount};
568  CS::Input0<kCount> input_0;
569  CS::Input1<kCount> input_1;
570  for (size_t i = 0; i < kCount; i++) {
571  input_0.elements[i] = Vector4(2.0 + i, 3.0 + i, 4.0 + i, 5.0 * i);
572  input_1.elements[i] = Vector4(6.0, 7.0, 8.0, 9.0);
573  }
574 
575  input_0.fixed_array[1] = IPoint32(2, 2);
576  input_1.fixed_array[0] = UintPoint32(3, 3);
577  input_0.some_int = 5;
578  input_1.some_struct = CS::SomeStruct{.vf = Point(3, 4), .i = 42};
579 
580  auto output_buffer = CreateHostVisibleDeviceBuffer<CS::Output<kCount>>(
581  context, "Output Buffer");
582 
583  CS::BindInfo(*pass, host_buffer->EmplaceUniform(info));
584  CS::BindInput0(*pass, host_buffer->EmplaceStorageBuffer(input_0));
585  CS::BindInput1(*pass, host_buffer->EmplaceStorageBuffer(input_1));
586  CS::BindOutput(*pass, DeviceBuffer::AsBufferView(output_buffer));
587 
588  // Intentionally making the workgroup count zero in one dimension. No GPU will
589  // tolerate this.
590  auto status = pass->Compute({0, 1, 1});
591  EXPECT_FALSE(status.ok());
592  EXPECT_EQ(status.code(), fml::StatusCode::kCancelled);
593  pass->EncodeCommands();
594 }
595 
596 } // namespace testing
597 } // namespace impeller
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
static std::shared_ptr< HostBuffer > Create(const std::shared_ptr< Allocator > &allocator, const std::shared_ptr< const IdleWaiter > &idle_waiter, size_t minimum_uniform_alignment)
Definition: host_buffer.cc:21
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
TEST_P(ComputeTest, ReturnsEarlyWhenAnyGridDimensionIsZero)
TEST_P(AiksTest, DrawAtlasNoColor)
INSTANTIATE_COMPUTE_SUITE(ComputeTest)
TPoint< Scalar > Point
Definition: point.h:426
TPoint< int32_t > IPoint32
Definition: point.h:428
TPoint< uint32_t > UintPoint32
Definition: point.h:429
std::shared_ptr< ContextGLES > context
std::shared_ptr< RenderPass > render_pass
An optional (but highly recommended) utility for creating pipelines from reflected shader information...
An optional (but highly recommended) utility for creating pipelines from reflected shader information...