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"
16 #include "impeller/renderer/prefix_sum_test.comp.h"
17 #include "impeller/renderer/threadgroup_sizing_test.comp.h"
25 auto context = GetContext();
27 ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
31 using CS = SampleComputeShader;
32 auto context = GetContext();
34 context->GetIdleWaiter());
36 ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
40 SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
41 ASSERT_TRUE(pipeline_desc.has_value());
42 auto compute_pipeline =
43 context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
44 ASSERT_TRUE(compute_pipeline);
46 auto cmd_buffer = context->CreateCommandBuffer();
47 auto pass = cmd_buffer->CreateComputePass();
48 ASSERT_TRUE(pass && pass->IsValid());
50 static constexpr
size_t kCount = 5;
52 pass->SetPipeline(compute_pipeline);
54 CS::Info info{.count = kCount};
55 CS::Input0<kCount> input_0;
56 CS::Input1<kCount> input_1;
57 for (
size_t i = 0; i < kCount; i++) {
58 input_0.elements[i] =
Vector4(2.0 + i, 3.0 + i, 4.0 + i, 5.0 * i);
59 input_1.elements[i] =
Vector4(6.0, 7.0, 8.0, 9.0);
62 input_0.fixed_array[1] =
IPoint32(2, 2);
65 input_1.some_struct = CS::SomeStruct{.vf =
Point(3, 4), .i = 42};
67 auto output_buffer = CreateHostVisibleDeviceBuffer<CS::Output<kCount>>(
68 context,
"Output Buffer");
70 CS::BindInfo(*pass, host_buffer->EmplaceUniform(info));
71 CS::BindInput0(*pass, host_buffer->EmplaceStorageBuffer(input_0));
72 CS::BindInput1(*pass, host_buffer->EmplaceStorageBuffer(input_1));
75 ASSERT_TRUE(pass->Compute(
ISize(kCount, 1)).ok());
76 ASSERT_TRUE(pass->EncodeCommands());
78 fml::AutoResetWaitableEvent latch;
80 context->GetCommandQueue()
83 [&latch, output_buffer, &input_0,
85 EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
87 auto view = DeviceBuffer::AsBufferView(output_buffer);
88 EXPECT_EQ(view.GetRange().length, sizeof(CS::Output<kCount>));
90 CS::Output<kCount>* output =
91 reinterpret_cast<CS::Output<kCount>*>(
92 output_buffer->OnGetContents());
94 for (size_t i = 0; i < kCount; i++) {
95 Vector4 vector = output->elements[i];
96 Vector4 computed = input_0.elements[i] * input_1.elements[i];
98 Vector4(computed.x + 2 + input_1.some_struct.i,
99 computed.y + 3 + input_1.some_struct.vf.x,
100 computed.z + 5 + input_1.some_struct.vf.y,
111 using CS = PrefixSumTestComputeShader;
112 auto context = GetContext();
114 context->GetIdleWaiter());
115 ASSERT_TRUE(context);
116 ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
120 SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
121 ASSERT_TRUE(pipeline_desc.has_value());
122 auto compute_pipeline =
123 context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
124 ASSERT_TRUE(compute_pipeline);
126 auto cmd_buffer = context->CreateCommandBuffer();
127 auto pass = cmd_buffer->CreateComputePass();
128 ASSERT_TRUE(pass && pass->IsValid());
130 static constexpr
size_t kCount = 5;
132 pass->SetPipeline(compute_pipeline);
134 CS::InputData<kCount> input_data;
135 input_data.count = kCount;
136 for (
size_t i = 0; i < kCount; i++) {
137 input_data.data[i] = 1 + i;
140 auto output_buffer = CreateHostVisibleDeviceBuffer<CS::OutputData<kCount>>(
141 context,
"Output Buffer");
143 CS::BindInputData(*pass, host_buffer->EmplaceStorageBuffer(input_data));
146 ASSERT_TRUE(pass->Compute(
ISize(kCount, 1)).ok());
147 ASSERT_TRUE(pass->EncodeCommands());
149 fml::AutoResetWaitableEvent latch;
151 context->GetCommandQueue()
152 ->Submit({cmd_buffer},
154 EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
156 auto view = DeviceBuffer::AsBufferView(output_buffer);
157 EXPECT_EQ(view.GetRange().length,
158 sizeof(CS::OutputData<kCount>));
160 CS::OutputData<kCount>* output =
161 reinterpret_cast<CS::OutputData<kCount>*>(
162 output_buffer->OnGetContents());
165 constexpr uint32_t expected[kCount] = {1, 3, 6, 10, 15};
166 for (
size_t i = 0; i < kCount; i++) {
167 auto computed_sum = output->data[i];
168 EXPECT_EQ(computed_sum, expected[i]);
178 using CS = ThreadgroupSizingTestComputeShader;
179 auto context = GetContext();
180 ASSERT_TRUE(context);
181 ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
185 SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
186 ASSERT_TRUE(pipeline_desc.has_value());
187 auto compute_pipeline =
188 context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
189 ASSERT_TRUE(compute_pipeline);
191 auto cmd_buffer = context->CreateCommandBuffer();
192 auto pass = cmd_buffer->CreateComputePass();
193 ASSERT_TRUE(pass && pass->IsValid());
195 static constexpr
size_t kCount = 2048;
197 pass->SetPipeline(compute_pipeline);
199 auto output_buffer = CreateHostVisibleDeviceBuffer<CS::OutputData<kCount>>(
200 context,
"Output Buffer");
202 CS::BindOutputData(*pass, DeviceBuffer::AsBufferView(output_buffer));
204 ASSERT_TRUE(pass->Compute(
ISize(kCount, 1)).ok());
205 ASSERT_TRUE(pass->EncodeCommands());
207 fml::AutoResetWaitableEvent latch;
209 context->GetCommandQueue()
210 ->Submit({cmd_buffer},
212 EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
214 auto view = DeviceBuffer::AsBufferView(output_buffer);
215 EXPECT_EQ(view.GetRange().length,
216 sizeof(CS::OutputData<kCount>));
218 CS::OutputData<kCount>* output =
219 reinterpret_cast<CS::OutputData<kCount>*>(
220 output_buffer->OnGetContents());
222 EXPECT_EQ(output->data[kCount - 1], kCount - 1);
231 using CS = PrefixSumTestComputeShader;
233 auto context = GetContext();
235 context->GetIdleWaiter());
237 ASSERT_TRUE(context);
238 ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
240 auto callback = [&](
RenderPass& render_pass) ->
bool {
243 SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
244 auto compute_pipeline =
245 context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
247 auto cmd_buffer = context->CreateCommandBuffer();
248 auto pass = cmd_buffer->CreateComputePass();
250 static constexpr
size_t kCount = 1023;
252 pass->SetPipeline(compute_pipeline);
254 CS::InputData<kCount> input_data;
255 input_data.count = kCount;
256 for (
size_t i = 0; i < kCount; i++) {
257 input_data.data[i] = 1 + i;
260 auto output_buffer = CreateHostVisibleDeviceBuffer<CS::OutputData<kCount>>(
261 context,
"Output Buffer");
263 CS::BindInputData(*pass, host_buffer->EmplaceStorageBuffer(input_data));
264 CS::BindOutputData(*pass, DeviceBuffer::AsBufferView(output_buffer));
266 pass->Compute(
ISize(kCount, 1));
267 pass->EncodeCommands();
268 host_buffer->Reset();
269 return context->GetCommandQueue()->Submit({cmd_buffer}).ok();
271 ASSERT_TRUE(OpenPlaygroundHere(callback));
275 using CS1 = Stage1ComputeShader;
277 using CS2 = Stage2ComputeShader;
280 auto context = GetContext();
282 context->GetIdleWaiter());
283 ASSERT_TRUE(context);
284 ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
286 auto pipeline_desc_1 =
287 Stage1PipelineBuilder::MakeDefaultPipelineDescriptor(*context);
288 ASSERT_TRUE(pipeline_desc_1.has_value());
289 auto compute_pipeline_1 =
290 context->GetPipelineLibrary()->GetPipeline(pipeline_desc_1).Get();
291 ASSERT_TRUE(compute_pipeline_1);
293 auto pipeline_desc_2 =
294 Stage2PipelineBuilder::MakeDefaultPipelineDescriptor(*context);
295 ASSERT_TRUE(pipeline_desc_2.has_value());
296 auto compute_pipeline_2 =
297 context->GetPipelineLibrary()->GetPipeline(pipeline_desc_2).Get();
298 ASSERT_TRUE(compute_pipeline_2);
300 auto cmd_buffer = context->CreateCommandBuffer();
301 auto pass = cmd_buffer->CreateComputePass();
302 ASSERT_TRUE(pass && pass->IsValid());
304 static constexpr
size_t kCount1 = 5;
305 static constexpr
size_t kCount2 =
kCount1 * 2;
307 CS1::Input<kCount1> input_1;
309 for (
size_t i = 0; i <
kCount1; i++) {
310 input_1.elements[i] = i;
313 CS2::Input<kCount2> input_2;
314 input_2.count = kCount2;
315 for (
size_t i = 0; i < kCount2; i++) {
316 input_2.elements[i] = i;
319 auto output_buffer_1 = CreateHostVisibleDeviceBuffer<CS1::Output<kCount2>>(
320 context,
"Output Buffer Stage 1");
321 auto output_buffer_2 = CreateHostVisibleDeviceBuffer<CS2::Output<kCount2>>(
322 context,
"Output Buffer Stage 2");
325 pass->SetPipeline(compute_pipeline_1);
327 CS1::BindInput(*pass, host_buffer->EmplaceStorageBuffer(input_1));
328 CS1::BindOutput(*pass, DeviceBuffer::AsBufferView(output_buffer_1));
330 ASSERT_TRUE(pass->Compute(
ISize(512, 1)).ok());
331 pass->AddBufferMemoryBarrier();
335 pass->SetPipeline(compute_pipeline_2);
337 CS1::BindInput(*pass, DeviceBuffer::AsBufferView(output_buffer_1));
338 CS2::BindOutput(*pass, DeviceBuffer::AsBufferView(output_buffer_2));
339 ASSERT_TRUE(pass->Compute(
ISize(512, 1)).ok());
342 ASSERT_TRUE(pass->EncodeCommands());
344 fml::AutoResetWaitableEvent latch;
346 context->GetCommandQueue()
347 ->Submit({cmd_buffer},
348 [&latch, &output_buffer_1,
350 EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
352 CS1::Output<kCount2>* output_1 =
353 reinterpret_cast<CS1::Output<kCount2>*>(
354 output_buffer_1->OnGetContents());
355 EXPECT_TRUE(output_1);
356 EXPECT_EQ(output_1->count, 10u);
359 ::testing::ElementsAre(0, 0, 2, 3, 4, 6, 6, 9, 8, 12));
361 CS2::Output<kCount2>* output_2 =
362 reinterpret_cast<CS2::Output<kCount2>*>(
363 output_buffer_2->OnGetContents());
364 EXPECT_TRUE(output_2);
365 EXPECT_EQ(output_2->count, 10u);
366 EXPECT_THAT(output_2->elements,
367 ::testing::ElementsAre(0, 0, 4, 6, 8, 12, 12,
378 using CS = SampleComputeShader;
379 auto context = GetContext();
381 context->GetIdleWaiter());
382 ASSERT_TRUE(context);
383 ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
387 SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
388 ASSERT_TRUE(pipeline_desc.has_value());
389 auto compute_pipeline =
390 context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
391 ASSERT_TRUE(compute_pipeline);
393 auto cmd_buffer = context->CreateCommandBuffer();
394 auto pass = cmd_buffer->CreateComputePass();
395 ASSERT_TRUE(pass && pass->IsValid());
397 static constexpr
size_t kCount = 5;
399 pass->SetPipeline(compute_pipeline);
401 CS::Info info{.count = kCount};
402 CS::Input0<kCount> input_0;
403 CS::Input1<kCount> input_1;
404 for (
size_t i = 0; i < kCount; i++) {
405 input_0.elements[i] =
Vector4(2.0 + i, 3.0 + i, 4.0 + i, 5.0 * i);
406 input_1.elements[i] =
Vector4(6.0, 7.0, 8.0, 9.0);
409 input_0.fixed_array[1] =
IPoint32(2, 2);
411 input_0.some_int = 5;
412 input_1.some_struct = CS::SomeStruct{.vf =
Point(3, 4), .i = 42};
414 auto output_buffer = CreateHostVisibleDeviceBuffer<CS::Output<kCount>>(
415 context,
"Output Buffer");
417 CS::BindInfo(*pass, host_buffer->EmplaceUniform(info));
418 CS::BindInput0(*pass, host_buffer->EmplaceStorageBuffer(input_0));
419 CS::BindInput1(*pass, host_buffer->EmplaceStorageBuffer(input_1));
420 CS::BindOutput(*pass, DeviceBuffer::AsBufferView(output_buffer));
422 ASSERT_TRUE(pass->Compute(
ISize(kCount, 1)).ok());
423 ASSERT_TRUE(pass->EncodeCommands());
425 fml::AutoResetWaitableEvent latch;
427 context->GetCommandQueue()
430 [&latch, output_buffer, &input_0,
432 EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
434 auto view = DeviceBuffer::AsBufferView(output_buffer);
435 EXPECT_EQ(view.GetRange().length, sizeof(CS::Output<kCount>));
437 CS::Output<kCount>* output =
438 reinterpret_cast<CS::Output<kCount>*>(
439 output_buffer->OnGetContents());
441 for (size_t i = 0; i < kCount; i++) {
442 Vector4 vector = output->elements[i];
443 Vector4 computed = input_0.elements[i] * input_1.elements[i];
445 Vector4(computed.x + 2 + input_1.some_struct.i,
446 computed.y + 3 + input_1.some_struct.vf.x,
447 computed.z + 5 + input_1.some_struct.vf.y,
458 using CS = SampleComputeShader;
459 auto context = GetContext();
461 context->GetIdleWaiter());
462 ASSERT_TRUE(context);
463 ASSERT_TRUE(context->GetCapabilities()->SupportsCompute());
467 SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
468 ASSERT_TRUE(pipeline_desc.has_value());
469 auto compute_pipeline =
470 context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
471 ASSERT_TRUE(compute_pipeline);
473 auto cmd_buffer = context->CreateCommandBuffer();
474 auto pass = cmd_buffer->CreateComputePass();
475 ASSERT_TRUE(pass && pass->IsValid());
477 static constexpr
size_t kCount = 5;
479 pass->SetPipeline(compute_pipeline);
481 CS::Info info{.count = kCount};
482 CS::Input0<kCount> input_0;
483 CS::Input1<kCount> input_1;
484 for (
size_t i = 0; i < kCount; i++) {
485 input_0.elements[i] =
Vector4(2.0 + i, 3.0 + i, 4.0 + i, 5.0 * i);
486 input_1.elements[i] =
Vector4(6.0, 7.0, 8.0, 9.0);
489 input_0.fixed_array[1] =
IPoint32(2, 2);
491 input_0.some_int = 5;
492 input_1.some_struct = CS::SomeStruct{.vf =
Point(3, 4), .i = 42};
494 auto output_buffer = CreateHostVisibleDeviceBuffer<CS::Output<kCount>>(
495 context,
"Output Buffer");
497 CS::BindInfo(*pass, host_buffer->EmplaceUniform(info));
498 CS::BindInput0(*pass, host_buffer->EmplaceStorageBuffer(input_0));
499 CS::BindInput1(*pass, host_buffer->EmplaceStorageBuffer(input_1));
500 CS::BindOutput(*pass, DeviceBuffer::AsBufferView(output_buffer));
504 EXPECT_FALSE(pass->Compute(
ISize(0, 1)).ok());
505 pass->EncodeCommands();
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)
Render passes encode render commands directed as one specific render target into an underlying comman...
ScopedObject< Object > Create(CtorArgs &&... args)
TEST_P(ComputeTest, ReturnsEarlyWhenAnyGridDimensionIsZero)
TEST_P(AiksTest, DrawAtlasNoColor)
INSTANTIATE_COMPUTE_SUITE(ComputeTest)
TPoint< int32_t > IPoint32
TPoint< uint32_t > UintPoint32
An optional (but highly recommended) utility for creating pipelines from reflected shader information...