1 // Copyright 2018 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/pipeline.h"
16
17 #include "gtest/gtest.h"
18 #include "src/make_unique.h"
19 #include "src/type_parser.h"
20
21 namespace amber {
22 namespace {
23
24 struct ShaderTypeData {
25 ShaderType type;
26 };
27
28 } // namespace
29
30 class PipelineTest : public testing::Test {
31 public:
TearDown()32 void TearDown() override {
33 color_buffer_ = nullptr;
34 depth_stencil_buffer_ = nullptr;
35 }
36
SetupColorAttachment(Pipeline * p,uint32_t location)37 void SetupColorAttachment(Pipeline* p, uint32_t location) {
38 if (!color_buffer_)
39 color_buffer_ = p->GenerateDefaultColorAttachmentBuffer();
40
41 p->AddColorAttachment(color_buffer_.get(), location, 0);
42 }
43
SetupDepthStencilAttachment(Pipeline * p)44 void SetupDepthStencilAttachment(Pipeline* p) {
45 if (!depth_stencil_buffer_)
46 depth_stencil_buffer_ = p->GenerateDefaultDepthStencilAttachmentBuffer();
47
48 p->SetDepthStencilBuffer(depth_stencil_buffer_.get());
49 }
50
51 private:
52 std::unique_ptr<Buffer> color_buffer_;
53 std::unique_ptr<Buffer> depth_stencil_buffer_;
54 };
55
TEST_F(PipelineTest,AddShader)56 TEST_F(PipelineTest, AddShader) {
57 Shader v(kShaderTypeVertex);
58 Shader f(kShaderTypeFragment);
59
60 Pipeline p(PipelineType::kGraphics);
61 Result r = p.AddShader(&v, kShaderTypeVertex);
62 ASSERT_TRUE(r.IsSuccess()) << r.Error();
63
64 r = p.AddShader(&f, kShaderTypeFragment);
65 ASSERT_TRUE(r.IsSuccess()) << r.Error();
66
67 const auto& shaders = p.GetShaders();
68 EXPECT_EQ(2U, shaders.size());
69
70 EXPECT_EQ(&v, shaders[0].GetShader());
71 EXPECT_EQ(&f, shaders[1].GetShader());
72 }
73
TEST_F(PipelineTest,MissingShader)74 TEST_F(PipelineTest, MissingShader) {
75 Pipeline p(PipelineType::kGraphics);
76 Result r = p.AddShader(nullptr, kShaderTypeVertex);
77 ASSERT_FALSE(r.IsSuccess());
78 EXPECT_EQ("shader can not be null when attached to pipeline", r.Error());
79 }
80
TEST_F(PipelineTest,DuplicateShaders)81 TEST_F(PipelineTest, DuplicateShaders) {
82 Shader v(kShaderTypeVertex);
83 Shader f(kShaderTypeFragment);
84
85 Pipeline p(PipelineType::kGraphics);
86 Result r = p.AddShader(&v, kShaderTypeVertex);
87 ASSERT_TRUE(r.IsSuccess()) << r.Error();
88
89 r = p.AddShader(&f, kShaderTypeFragment);
90 ASSERT_TRUE(r.IsSuccess()) << r.Error();
91
92 r = p.AddShader(&v, kShaderTypeVertex);
93 ASSERT_FALSE(r.IsSuccess());
94 EXPECT_EQ("can not add duplicate shader to pipeline", r.Error());
95 }
96
97 using AmberScriptPipelineComputePipelineTest =
98 testing::TestWithParam<ShaderTypeData>;
TEST_P(AmberScriptPipelineComputePipelineTest,SettingGraphicsShaderToComputePipeline)99 TEST_P(AmberScriptPipelineComputePipelineTest,
100 SettingGraphicsShaderToComputePipeline) {
101 const auto test_data = GetParam();
102
103 Shader s(test_data.type);
104
105 Pipeline p(PipelineType::kCompute);
106 Result r = p.AddShader(&s, test_data.type);
107 ASSERT_FALSE(r.IsSuccess());
108 EXPECT_EQ("only compute shaders allowed in a compute pipeline", r.Error());
109 }
110 INSTANTIATE_TEST_SUITE_P(
111 AmberScriptPipelineComputePipelineTests,
112 AmberScriptPipelineComputePipelineTest,
113 testing::Values(
114 ShaderTypeData{kShaderTypeVertex},
115 ShaderTypeData{kShaderTypeFragment},
116 ShaderTypeData{kShaderTypeGeometry},
117 ShaderTypeData{kShaderTypeTessellationEvaluation},
118 ShaderTypeData{
119 kShaderTypeTessellationControl})); // NOLINT(whitespace/parens)
120
TEST_F(PipelineTest,SettingComputeShaderToGraphicsPipeline)121 TEST_F(PipelineTest, SettingComputeShaderToGraphicsPipeline) {
122 Shader c(kShaderTypeCompute);
123
124 Pipeline p(PipelineType::kGraphics);
125 Result r = p.AddShader(&c, kShaderTypeCompute);
126 ASSERT_FALSE(r.IsSuccess());
127 EXPECT_EQ("can not add a compute shader to a graphics pipeline", r.Error());
128 }
129
TEST_F(PipelineTest,SetShaderOptimizations)130 TEST_F(PipelineTest, SetShaderOptimizations) {
131 Shader v(kShaderTypeVertex);
132 Shader f(kShaderTypeFragment);
133
134 Pipeline p(PipelineType::kGraphics);
135 Result r = p.AddShader(&v, kShaderTypeVertex);
136 ASSERT_TRUE(r.IsSuccess()) << r.Error();
137
138 r = p.AddShader(&f, kShaderTypeFragment);
139 ASSERT_TRUE(r.IsSuccess()) << r.Error();
140
141 std::vector<std::string> first = {"First", "Second"};
142 std::vector<std::string> second = {"Third", "Forth"};
143
144 r = p.SetShaderOptimizations(&f, first);
145 ASSERT_TRUE(r.IsSuccess()) << r.Error();
146
147 r = p.SetShaderOptimizations(&v, second);
148 ASSERT_TRUE(r.IsSuccess()) << r.Error();
149
150 const auto& shaders = p.GetShaders();
151 EXPECT_EQ(2U, shaders.size());
152 EXPECT_EQ(second, shaders[0].GetShaderOptimizations());
153 EXPECT_EQ(first, shaders[1].GetShaderOptimizations());
154 }
155
TEST_F(PipelineTest,DuplicateShaderOptimizations)156 TEST_F(PipelineTest, DuplicateShaderOptimizations) {
157 Shader v(kShaderTypeVertex);
158
159 Pipeline p(PipelineType::kGraphics);
160 Result r = p.AddShader(&v, kShaderTypeVertex);
161 ASSERT_TRUE(r.IsSuccess()) << r.Error();
162
163 std::vector<std::string> data = {"One", "One"};
164 r = p.SetShaderOptimizations(&v, data);
165 ASSERT_FALSE(r.IsSuccess());
166 EXPECT_EQ("duplicate optimization flag (One) set on shader", r.Error());
167 }
168
TEST_F(PipelineTest,SetOptimizationForMissingShader)169 TEST_F(PipelineTest, SetOptimizationForMissingShader) {
170 Pipeline p(PipelineType::kGraphics);
171 Result r = p.SetShaderOptimizations(nullptr, {"One", "Two"});
172 ASSERT_FALSE(r.IsSuccess());
173 EXPECT_EQ("invalid shader specified for optimizations", r.Error());
174 }
175
TEST_F(PipelineTest,SetOptimizationForInvalidShader)176 TEST_F(PipelineTest, SetOptimizationForInvalidShader) {
177 Shader v(kShaderTypeVertex);
178 v.SetName("my_shader");
179
180 Pipeline p(PipelineType::kGraphics);
181 Result r = p.SetShaderOptimizations(&v, {"One", "Two"});
182 ASSERT_FALSE(r.IsSuccess());
183 EXPECT_EQ("unknown shader specified for optimizations: my_shader", r.Error());
184 }
185
TEST_F(PipelineTest,GraphicsPipelineRequiresColorAttachment)186 TEST_F(PipelineTest, GraphicsPipelineRequiresColorAttachment) {
187 Pipeline p(PipelineType::kGraphics);
188 SetupDepthStencilAttachment(&p);
189
190 Result r = p.Validate();
191 ASSERT_FALSE(r.IsSuccess());
192 EXPECT_EQ("PIPELINE missing color attachment", r.Error());
193 }
194
TEST_F(PipelineTest,GraphicsPipelineRequiresVertexAndFragmentShader)195 TEST_F(PipelineTest, GraphicsPipelineRequiresVertexAndFragmentShader) {
196 Shader v(kShaderTypeVertex);
197 Shader f(kShaderTypeFragment);
198 Shader g(kShaderTypeGeometry);
199
200 Pipeline p(PipelineType::kGraphics);
201 SetupColorAttachment(&p, 0);
202 SetupDepthStencilAttachment(&p);
203
204 Result r = p.AddShader(&v, kShaderTypeVertex);
205 EXPECT_TRUE(r.IsSuccess()) << r.Error();
206
207 r = p.AddShader(&g, kShaderTypeGeometry);
208 EXPECT_TRUE(r.IsSuccess()) << r.Error();
209
210 r = p.AddShader(&f, kShaderTypeFragment);
211 EXPECT_TRUE(r.IsSuccess()) << r.Error();
212
213 r = p.Validate();
214 EXPECT_TRUE(r.IsSuccess()) << r.Error();
215 }
216
TEST_F(PipelineTest,GraphicsPipelineMissingVertexShader)217 TEST_F(PipelineTest, GraphicsPipelineMissingVertexShader) {
218 Shader f(kShaderTypeFragment);
219 Shader g(kShaderTypeGeometry);
220
221 Pipeline p(PipelineType::kGraphics);
222 SetupColorAttachment(&p, 0);
223 SetupDepthStencilAttachment(&p);
224
225 Result r = p.AddShader(&g, kShaderTypeGeometry);
226 EXPECT_TRUE(r.IsSuccess()) << r.Error();
227
228 r = p.AddShader(&f, kShaderTypeFragment);
229 EXPECT_TRUE(r.IsSuccess()) << r.Error();
230
231 r = p.Validate();
232 EXPECT_FALSE(r.IsSuccess()) << r.Error();
233 EXPECT_EQ("graphics pipeline requires a vertex shader", r.Error());
234 }
235
TEST_F(PipelineTest,ComputePipelineRequiresComputeShader)236 TEST_F(PipelineTest, ComputePipelineRequiresComputeShader) {
237 Shader c(kShaderTypeCompute);
238
239 Pipeline p(PipelineType::kCompute);
240 SetupColorAttachment(&p, 0);
241 SetupDepthStencilAttachment(&p);
242
243 Result r = p.AddShader(&c, kShaderTypeCompute);
244 EXPECT_TRUE(r.IsSuccess()) << r.Error();
245
246 r = p.Validate();
247 EXPECT_TRUE(r.IsSuccess()) << r.Error();
248 }
249
TEST_F(PipelineTest,ComputePipelineWithoutShader)250 TEST_F(PipelineTest, ComputePipelineWithoutShader) {
251 Pipeline p(PipelineType::kCompute);
252 SetupColorAttachment(&p, 0);
253 SetupDepthStencilAttachment(&p);
254
255 Result r = p.Validate();
256 EXPECT_FALSE(r.IsSuccess()) << r.Error();
257 EXPECT_EQ("compute pipeline requires a compute shader", r.Error());
258 }
259
TEST_F(PipelineTest,PipelineBufferWithoutFormat)260 TEST_F(PipelineTest, PipelineBufferWithoutFormat) {
261 Pipeline p(PipelineType::kCompute);
262
263 auto buf = MakeUnique<Buffer>();
264 buf->SetName("MyBuffer");
265 p.AddBuffer(buf.get(), BufferType::kStorage, 0, 0, 0, 0, 0, 0);
266
267 Result r = p.Validate();
268 EXPECT_FALSE(r.IsSuccess()) << r.Error();
269 EXPECT_EQ("buffer (0:0) requires a format", r.Error());
270 }
271
TEST_F(PipelineTest,SetEntryPointForMissingShader)272 TEST_F(PipelineTest, SetEntryPointForMissingShader) {
273 Shader c(kShaderTypeCompute);
274 c.SetName("my_shader");
275
276 Pipeline p(PipelineType::kCompute);
277 Result r = p.SetShaderEntryPoint(&c, "test");
278 EXPECT_FALSE(r.IsSuccess());
279 EXPECT_EQ("unknown shader specified for entry point: my_shader", r.Error());
280 }
281
TEST_F(PipelineTest,SetEntryPointForNullShader)282 TEST_F(PipelineTest, SetEntryPointForNullShader) {
283 Pipeline p(PipelineType::kCompute);
284 Result r = p.SetShaderEntryPoint(nullptr, "test");
285 EXPECT_FALSE(r.IsSuccess());
286 EXPECT_EQ("invalid shader specified for entry point", r.Error());
287 }
288
TEST_F(PipelineTest,SetBlankEntryPoint)289 TEST_F(PipelineTest, SetBlankEntryPoint) {
290 Shader c(kShaderTypeCompute);
291 Pipeline p(PipelineType::kCompute);
292 Result r = p.AddShader(&c, kShaderTypeCompute);
293 ASSERT_TRUE(r.IsSuccess()) << r.Error();
294
295 r = p.SetShaderEntryPoint(&c, "");
296 EXPECT_FALSE(r.IsSuccess());
297 EXPECT_EQ("entry point should not be blank", r.Error());
298 }
299
TEST_F(PipelineTest,ShaderDefaultEntryPoint)300 TEST_F(PipelineTest, ShaderDefaultEntryPoint) {
301 Shader c(kShaderTypeCompute);
302 Pipeline p(PipelineType::kCompute);
303 Result r = p.AddShader(&c, kShaderTypeCompute);
304 ASSERT_TRUE(r.IsSuccess()) << r.Error();
305
306 const auto& shaders = p.GetShaders();
307 ASSERT_EQ(1U, shaders.size());
308 EXPECT_EQ("main", shaders[0].GetEntryPoint());
309 }
310
TEST_F(PipelineTest,SetShaderEntryPoint)311 TEST_F(PipelineTest, SetShaderEntryPoint) {
312 Shader c(kShaderTypeCompute);
313 Pipeline p(PipelineType::kCompute);
314 Result r = p.AddShader(&c, kShaderTypeCompute);
315 ASSERT_TRUE(r.IsSuccess()) << r.Error();
316
317 r = p.SetShaderEntryPoint(&c, "my_main");
318 ASSERT_TRUE(r.IsSuccess()) << r.Error();
319
320 const auto& shaders = p.GetShaders();
321 ASSERT_EQ(1U, shaders.size());
322 EXPECT_EQ("my_main", shaders[0].GetEntryPoint());
323 }
324
TEST_F(PipelineTest,SetEntryPointMulitpleTimes)325 TEST_F(PipelineTest, SetEntryPointMulitpleTimes) {
326 Shader c(kShaderTypeCompute);
327 Pipeline p(PipelineType::kCompute);
328 Result r = p.AddShader(&c, kShaderTypeCompute);
329 ASSERT_TRUE(r.IsSuccess()) << r.Error();
330
331 r = p.SetShaderEntryPoint(&c, "my_main");
332 ASSERT_TRUE(r.IsSuccess()) << r.Error();
333
334 r = p.SetShaderEntryPoint(&c, "another_main");
335 EXPECT_FALSE(r.IsSuccess());
336 EXPECT_EQ("multiple entry points given for the same shader", r.Error());
337 }
338
TEST_F(PipelineTest,Clone)339 TEST_F(PipelineTest, Clone) {
340 Pipeline p(PipelineType::kGraphics);
341 p.SetName("my_pipeline");
342 p.SetFramebufferWidth(800);
343 p.SetFramebufferHeight(600);
344
345 SetupColorAttachment(&p, 0);
346 SetupDepthStencilAttachment(&p);
347
348 Shader f(kShaderTypeFragment);
349 p.AddShader(&f, kShaderTypeFragment);
350 Shader v(kShaderTypeVertex);
351 p.AddShader(&v, kShaderTypeVertex);
352 p.SetShaderEntryPoint(&v, "my_main");
353
354 auto vtex_buf = MakeUnique<Buffer>();
355 vtex_buf->SetName("vertex_buffer");
356 TypeParser parser;
357 auto int_type = parser.Parse("R32_SINT");
358 auto int_fmt = MakeUnique<Format>(int_type.get());
359 p.AddVertexBuffer(vtex_buf.get(), 1, InputRate::kVertex, int_fmt.get(), 5,
360 10);
361
362 auto idx_buf = MakeUnique<Buffer>();
363 idx_buf->SetName("Index Buffer");
364 p.SetIndexBuffer(idx_buf.get());
365
366 auto buf1 = MakeUnique<Buffer>();
367 buf1->SetName("buf1");
368 p.AddBuffer(buf1.get(), BufferType::kStorage, 1, 1, 0, 0, 0, 0);
369
370 auto buf2 = MakeUnique<Buffer>();
371 buf2->SetName("buf2");
372 p.AddBuffer(buf2.get(), BufferType::kStorage, 1, 2, 0, 16, 256, 512);
373
374 auto clone = p.Clone();
375 EXPECT_EQ("", clone->GetName());
376 EXPECT_EQ(800U, clone->GetFramebufferWidth());
377 EXPECT_EQ(600U, clone->GetFramebufferHeight());
378
379 auto shaders = clone->GetShaders();
380 ASSERT_EQ(2U, shaders.size());
381 EXPECT_EQ(kShaderTypeFragment, shaders[0].GetShaderType());
382 EXPECT_EQ(kShaderTypeVertex, shaders[1].GetShaderType());
383 EXPECT_EQ("my_main", shaders[1].GetEntryPoint());
384
385 ASSERT_TRUE(clone->GetIndexBuffer() != nullptr);
386 EXPECT_EQ("Index Buffer", clone->GetIndexBuffer()->GetName());
387
388 auto vtex_buffers = clone->GetVertexBuffers();
389 ASSERT_EQ(1U, vtex_buffers.size());
390 EXPECT_EQ(1, vtex_buffers[0].location);
391 EXPECT_EQ("vertex_buffer", vtex_buffers[0].buffer->GetName());
392 EXPECT_EQ(InputRate::kVertex, vtex_buffers[0].input_rate);
393 EXPECT_EQ(FormatType::kR32_SINT, vtex_buffers[0].format->GetFormatType());
394 EXPECT_EQ(5, vtex_buffers[0].offset);
395 EXPECT_EQ(10, vtex_buffers[0].stride);
396
397 auto bufs = clone->GetBuffers();
398 ASSERT_EQ(2U, bufs.size());
399 EXPECT_EQ("buf1", bufs[0].buffer->GetName());
400 EXPECT_EQ(1U, bufs[0].descriptor_set);
401 EXPECT_EQ(1U, bufs[0].binding);
402 EXPECT_EQ(0U, bufs[0].dynamic_offset);
403 EXPECT_EQ(0U, bufs[0].descriptor_offset);
404 EXPECT_EQ(0U, bufs[0].descriptor_range);
405
406 EXPECT_EQ("buf2", bufs[1].buffer->GetName());
407 EXPECT_EQ(1U, bufs[1].descriptor_set);
408 EXPECT_EQ(2U, bufs[1].binding);
409 EXPECT_EQ(16U, bufs[1].dynamic_offset);
410 EXPECT_EQ(256U, bufs[1].descriptor_offset);
411 EXPECT_EQ(512U, bufs[1].descriptor_range);
412 }
413
TEST_F(PipelineTest,OpenCLUpdateBindings)414 TEST_F(PipelineTest, OpenCLUpdateBindings) {
415 Pipeline p(PipelineType::kCompute);
416 p.SetName("my_pipeline");
417
418 Shader cs(kShaderTypeCompute);
419 cs.SetFormat(kShaderFormatOpenCLC);
420 p.AddShader(&cs, kShaderTypeCompute);
421 p.SetShaderEntryPoint(&cs, "my_main");
422
423 Pipeline::ShaderInfo::DescriptorMapEntry entry1;
424 entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO;
425 entry1.descriptor_set = 4;
426 entry1.binding = 5;
427 entry1.arg_name = "arg_a";
428 entry1.arg_ordinal = 0;
429 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
430
431 Pipeline::ShaderInfo::DescriptorMapEntry entry2;
432 entry2.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO;
433 entry2.descriptor_set = 3;
434 entry2.binding = 1;
435 entry2.arg_name = "arg_b";
436 entry2.arg_ordinal = 1;
437 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
438
439 auto a_buf = MakeUnique<Buffer>();
440 a_buf->SetName("buf1");
441 p.AddBuffer(a_buf.get(), BufferType::kStorage, "arg_a");
442
443 auto b_buf = MakeUnique<Buffer>();
444 b_buf->SetName("buf2");
445 p.AddBuffer(b_buf.get(), BufferType::kStorage, 1);
446
447 p.UpdateOpenCLBufferBindings();
448
449 auto& bufs = p.GetBuffers();
450 ASSERT_EQ(2U, bufs.size());
451 EXPECT_EQ("buf1", bufs[0].buffer->GetName());
452 EXPECT_EQ(4U, bufs[0].descriptor_set);
453 EXPECT_EQ(5U, bufs[0].binding);
454 EXPECT_EQ("buf2", bufs[1].buffer->GetName());
455 EXPECT_EQ(3U, bufs[1].descriptor_set);
456 EXPECT_EQ(1U, bufs[1].binding);
457 }
458
TEST_F(PipelineTest,OpenCLUpdateBindingTypeMismatch)459 TEST_F(PipelineTest, OpenCLUpdateBindingTypeMismatch) {
460 Pipeline p(PipelineType::kCompute);
461 p.SetName("my_pipeline");
462
463 Shader cs(kShaderTypeCompute);
464 cs.SetFormat(kShaderFormatOpenCLC);
465 p.AddShader(&cs, kShaderTypeCompute);
466 p.SetShaderEntryPoint(&cs, "my_main");
467
468 Pipeline::ShaderInfo::DescriptorMapEntry entry1;
469 entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO;
470 entry1.descriptor_set = 4;
471 entry1.binding = 5;
472 entry1.arg_name = "arg_a";
473 entry1.arg_ordinal = 0;
474 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
475
476 Pipeline::ShaderInfo::DescriptorMapEntry entry2;
477 entry2.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO;
478 entry2.descriptor_set = 3;
479 entry2.binding = 1;
480 entry2.arg_name = "arg_b";
481 entry2.arg_ordinal = 1;
482 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
483
484 auto a_buf = MakeUnique<Buffer>();
485 a_buf->SetName("buf1");
486 p.AddBuffer(a_buf.get(), BufferType::kStorage, "arg_a");
487
488 auto b_buf = MakeUnique<Buffer>();
489 b_buf->SetName("buf2");
490 p.AddBuffer(b_buf.get(), BufferType::kUniform, 1);
491
492 auto r = p.UpdateOpenCLBufferBindings();
493
494 ASSERT_FALSE(r.IsSuccess());
495 EXPECT_EQ("Buffer buf2 must be a uniform binding", r.Error());
496 }
497
TEST_F(PipelineTest,OpenCLUpdateBindingImagesAndSamplers)498 TEST_F(PipelineTest, OpenCLUpdateBindingImagesAndSamplers) {
499 Pipeline p(PipelineType::kCompute);
500 p.SetName("my_pipeline");
501
502 Shader cs(kShaderTypeCompute);
503 cs.SetFormat(kShaderFormatOpenCLC);
504 p.AddShader(&cs, kShaderTypeCompute);
505 p.SetShaderEntryPoint(&cs, "my_main");
506
507 Pipeline::ShaderInfo::DescriptorMapEntry entry1;
508 entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE;
509 entry1.descriptor_set = 4;
510 entry1.binding = 5;
511 entry1.arg_name = "arg_a";
512 entry1.arg_ordinal = 0;
513 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
514
515 Pipeline::ShaderInfo::DescriptorMapEntry entry2;
516 entry2.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE;
517 entry2.descriptor_set = 3;
518 entry2.binding = 1;
519 entry2.arg_name = "arg_b";
520 entry2.arg_ordinal = 1;
521 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
522
523 Pipeline::ShaderInfo::DescriptorMapEntry entry3;
524 entry2.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SAMPLER;
525 entry2.descriptor_set = 3;
526 entry2.binding = 2;
527 entry2.arg_name = "arg_c";
528 entry2.arg_ordinal = 2;
529 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
530
531 auto a_buf = MakeUnique<Buffer>();
532 a_buf->SetName("buf1");
533 p.AddBuffer(a_buf.get(), BufferType::kSampledImage, "arg_a");
534
535 auto b_buf = MakeUnique<Buffer>();
536 b_buf->SetName("buf2");
537 p.AddBuffer(b_buf.get(), BufferType::kStorageImage, 1);
538
539 auto s = MakeUnique<Sampler>();
540 s->SetName("samp");
541 p.AddSampler(s.get(), "arg_c");
542
543 auto r = p.UpdateOpenCLBufferBindings();
544
545 ASSERT_TRUE(r.IsSuccess());
546 }
547
TEST_F(PipelineTest,OpenCLGeneratePodBuffers)548 TEST_F(PipelineTest, OpenCLGeneratePodBuffers) {
549 Pipeline p(PipelineType::kCompute);
550 p.SetName("my_pipeline");
551
552 Shader cs(kShaderTypeCompute);
553 cs.SetFormat(kShaderFormatOpenCLC);
554 p.AddShader(&cs, kShaderTypeCompute);
555 p.SetShaderEntryPoint(&cs, "my_main");
556
557 // Descriptor map.
558 Pipeline::ShaderInfo::DescriptorMapEntry entry1;
559 entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
560 entry1.descriptor_set = 4;
561 entry1.binding = 5;
562 entry1.arg_name = "arg_a";
563 entry1.arg_ordinal = 0;
564 entry1.pod_offset = 0;
565 entry1.pod_arg_size = 4;
566 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
567
568 Pipeline::ShaderInfo::DescriptorMapEntry entry2;
569 entry2.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
570 entry2.descriptor_set = 4;
571 entry2.binding = 5;
572 entry2.arg_name = "arg_b";
573 entry2.arg_ordinal = 0;
574 entry2.pod_offset = 4;
575 entry2.pod_arg_size = 1;
576 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
577
578 Pipeline::ShaderInfo::DescriptorMapEntry entry3;
579 entry3.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
580 entry3.descriptor_set = 4;
581 entry3.binding = 4;
582 entry3.arg_name = "arg_c";
583 entry3.arg_ordinal = 0;
584 entry3.pod_offset = 0;
585 entry3.pod_arg_size = 4;
586 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry3));
587
588 // Set commands.
589 Value int_value;
590 int_value.SetIntValue(1);
591
592 TypeParser parser;
593 auto int_type = parser.Parse("R32_SINT");
594 auto int_fmt = MakeUnique<Format>(int_type.get());
595 auto char_type = parser.Parse("R8_SINT");
596 auto char_fmt = MakeUnique<Format>(char_type.get());
597
598 Pipeline::ArgSetInfo arg_info1;
599 arg_info1.name = "arg_a";
600 arg_info1.ordinal = 99;
601 arg_info1.fmt = int_fmt.get();
602 arg_info1.value = int_value;
603 p.SetArg(std::move(arg_info1));
604
605 Pipeline::ArgSetInfo arg_info2;
606 arg_info2.name = "arg_b";
607 arg_info2.ordinal = 99;
608 arg_info2.fmt = char_fmt.get();
609 arg_info2.value = int_value;
610 p.SetArg(std::move(arg_info2));
611
612 Pipeline::ArgSetInfo arg_info3;
613 arg_info3.name = "arg_c";
614 arg_info3.ordinal = 99;
615 arg_info3.fmt = int_fmt.get();
616 arg_info3.value = int_value;
617 p.SetArg(std::move(arg_info3));
618
619 auto r = p.GenerateOpenCLPodBuffers();
620 ASSERT_TRUE(r.IsSuccess());
621 EXPECT_EQ(2U, p.GetBuffers().size());
622
623 const auto& b1 = p.GetBuffers()[0];
624 EXPECT_EQ(4U, b1.descriptor_set);
625 EXPECT_EQ(5U, b1.binding);
626 EXPECT_EQ(5U, b1.buffer->ValueCount());
627
628 const auto& b2 = p.GetBuffers()[1];
629 EXPECT_EQ(4U, b2.descriptor_set);
630 EXPECT_EQ(4U, b2.binding);
631 EXPECT_EQ(4U, b2.buffer->ValueCount());
632 }
633
TEST_F(PipelineTest,OpenCLGeneratePodBuffersBadName)634 TEST_F(PipelineTest, OpenCLGeneratePodBuffersBadName) {
635 Pipeline p(PipelineType::kCompute);
636 p.SetName("my_pipeline");
637
638 Shader cs(kShaderTypeCompute);
639 cs.SetFormat(kShaderFormatOpenCLC);
640 p.AddShader(&cs, kShaderTypeCompute);
641 p.SetShaderEntryPoint(&cs, "my_main");
642
643 // Descriptor map.
644 Pipeline::ShaderInfo::DescriptorMapEntry entry1;
645 entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
646 entry1.descriptor_set = 4;
647 entry1.binding = 5;
648 entry1.arg_name = "arg_a";
649 entry1.arg_ordinal = 0;
650 entry1.pod_offset = 0;
651 entry1.pod_arg_size = 4;
652 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
653
654 // Set commands.
655 Value int_value;
656 int_value.SetIntValue(1);
657
658 TypeParser parser;
659 auto int_type = parser.Parse("R32_SINT");
660 auto int_fmt = MakeUnique<Format>(int_type.get());
661
662 Pipeline::ArgSetInfo arg_info1;
663 arg_info1.name = "arg_z";
664 arg_info1.ordinal = 99;
665 arg_info1.fmt = int_fmt.get();
666 arg_info1.value = int_value;
667 p.SetArg(std::move(arg_info1));
668
669 auto r = p.GenerateOpenCLPodBuffers();
670 ASSERT_FALSE(r.IsSuccess());
671 EXPECT_EQ(
672 "could not find descriptor map entry for SET command: kernel my_main, "
673 "name arg_z",
674 r.Error());
675 }
676
TEST_F(PipelineTest,OpenCLGeneratePodBuffersBadSize)677 TEST_F(PipelineTest, OpenCLGeneratePodBuffersBadSize) {
678 Pipeline p(PipelineType::kCompute);
679 p.SetName("my_pipeline");
680
681 Shader cs(kShaderTypeCompute);
682 cs.SetFormat(kShaderFormatOpenCLC);
683 p.AddShader(&cs, kShaderTypeCompute);
684 p.SetShaderEntryPoint(&cs, "my_main");
685
686 // Descriptor map.
687 Pipeline::ShaderInfo::DescriptorMapEntry entry1;
688 entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
689 entry1.descriptor_set = 4;
690 entry1.binding = 5;
691 entry1.arg_name = "arg_a";
692 entry1.arg_ordinal = 0;
693 entry1.pod_offset = 0;
694 entry1.pod_arg_size = 4;
695 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
696
697 // Set commands.
698 Value int_value;
699 int_value.SetIntValue(1);
700
701 TypeParser parser;
702 auto short_type = parser.Parse("R16_SINT");
703 auto short_fmt = MakeUnique<Format>(short_type.get());
704
705 Pipeline::ArgSetInfo arg_info1;
706 arg_info1.name = "";
707 arg_info1.ordinal = 0;
708 arg_info1.fmt = short_fmt.get();
709 arg_info1.value = int_value;
710 p.SetArg(std::move(arg_info1));
711
712 auto r = p.GenerateOpenCLPodBuffers();
713 ASSERT_FALSE(r.IsSuccess());
714 EXPECT_EQ("SET command uses incorrect data size: kernel my_main, number 0",
715 r.Error());
716 }
717
TEST_F(PipelineTest,OpenCLClone)718 TEST_F(PipelineTest, OpenCLClone) {
719 Pipeline p(PipelineType::kCompute);
720 p.SetName("my_pipeline");
721
722 Shader cs(kShaderTypeCompute);
723 cs.SetFormat(kShaderFormatOpenCLC);
724 p.AddShader(&cs, kShaderTypeCompute);
725 p.SetShaderEntryPoint(&cs, "my_main");
726
727 // Descriptor map.
728 Pipeline::ShaderInfo::DescriptorMapEntry entry1;
729 entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
730 entry1.descriptor_set = 4;
731 entry1.binding = 5;
732 entry1.arg_name = "arg_a";
733 entry1.arg_ordinal = 0;
734 entry1.pod_offset = 0;
735 entry1.pod_arg_size = 4;
736 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
737
738 Pipeline::ShaderInfo::DescriptorMapEntry entry2;
739 entry2.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
740 entry2.descriptor_set = 4;
741 entry2.binding = 5;
742 entry2.arg_name = "arg_b";
743 entry2.arg_ordinal = 0;
744 entry2.pod_offset = 4;
745 entry2.pod_arg_size = 1;
746 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
747
748 Pipeline::ShaderInfo::DescriptorMapEntry entry3;
749 entry3.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
750 entry3.descriptor_set = 4;
751 entry3.binding = 4;
752 entry3.arg_name = "arg_c";
753 entry3.arg_ordinal = 0;
754 entry3.pod_offset = 0;
755 entry3.pod_arg_size = 4;
756 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry3));
757
758 // Set commands.
759 Value int_value;
760 int_value.SetIntValue(1);
761
762 TypeParser parser;
763 auto int_type = parser.Parse("R32_SINT");
764 auto int_fmt = MakeUnique<Format>(int_type.get());
765 auto char_type = parser.Parse("R8_SINT");
766 auto char_fmt = MakeUnique<Format>(char_type.get());
767
768 Pipeline::ArgSetInfo arg_info1;
769 arg_info1.name = "arg_a";
770 arg_info1.ordinal = 99;
771 arg_info1.fmt = int_fmt.get();
772 arg_info1.value = int_value;
773 p.SetArg(std::move(arg_info1));
774
775 Pipeline::ArgSetInfo arg_info2;
776 arg_info2.name = "arg_b";
777 arg_info2.ordinal = 99;
778 arg_info2.fmt = char_fmt.get();
779 arg_info2.value = int_value;
780 p.SetArg(std::move(arg_info2));
781
782 Pipeline::ArgSetInfo arg_info3;
783 arg_info3.name = "arg_c";
784 arg_info3.ordinal = 99;
785 arg_info3.fmt = int_fmt.get();
786 arg_info3.value = int_value;
787 p.SetArg(std::move(arg_info3));
788
789 auto clone = p.Clone();
790 auto r = clone->GenerateOpenCLPodBuffers();
791 ASSERT_TRUE(r.IsSuccess());
792 EXPECT_EQ(3U, clone->SetArgValues().size());
793 EXPECT_EQ(2U, clone->GetBuffers().size());
794
795 const auto& b1 = clone->GetBuffers()[0];
796 EXPECT_EQ(4U, b1.descriptor_set);
797 EXPECT_EQ(5U, b1.binding);
798 EXPECT_EQ(5U, b1.buffer->ValueCount());
799
800 const auto& b2 = clone->GetBuffers()[1];
801 EXPECT_EQ(4U, b2.descriptor_set);
802 EXPECT_EQ(4U, b2.binding);
803 EXPECT_EQ(4U, b2.buffer->ValueCount());
804 }
805
TEST_F(PipelineTest,OpenCLGenerateLiteralSamplers)806 TEST_F(PipelineTest, OpenCLGenerateLiteralSamplers) {
807 Pipeline p(PipelineType::kCompute);
808 p.SetName("my_pipeline");
809
810 p.AddSampler(16, 0, 0);
811 p.AddSampler(41, 0, 1);
812
813 auto r = p.GenerateOpenCLLiteralSamplers();
814 ASSERT_TRUE(r.IsSuccess());
815 for (auto& info : p.GetSamplers()) {
816 if (info.mask == 16) {
817 EXPECT_NE(nullptr, info.sampler);
818 EXPECT_EQ(FilterType::kNearest, info.sampler->GetMagFilter());
819 EXPECT_EQ(FilterType::kNearest, info.sampler->GetMinFilter());
820 EXPECT_EQ(AddressMode::kClampToEdge, info.sampler->GetAddressModeU());
821 EXPECT_EQ(AddressMode::kClampToEdge, info.sampler->GetAddressModeV());
822 EXPECT_EQ(AddressMode::kClampToEdge, info.sampler->GetAddressModeW());
823 EXPECT_EQ(0.0f, info.sampler->GetMinLOD());
824 EXPECT_EQ(0.0f, info.sampler->GetMaxLOD());
825 } else {
826 EXPECT_NE(nullptr, info.sampler);
827 EXPECT_EQ(FilterType::kLinear, info.sampler->GetMagFilter());
828 EXPECT_EQ(FilterType::kLinear, info.sampler->GetMinFilter());
829 EXPECT_EQ(AddressMode::kMirroredRepeat, info.sampler->GetAddressModeU());
830 EXPECT_EQ(AddressMode::kMirroredRepeat, info.sampler->GetAddressModeV());
831 EXPECT_EQ(AddressMode::kMirroredRepeat, info.sampler->GetAddressModeW());
832 EXPECT_EQ(0.0f, info.sampler->GetMinLOD());
833 EXPECT_EQ(0.0f, info.sampler->GetMaxLOD());
834 }
835 }
836 }
837
TEST_F(PipelineTest,OpenCLGeneratePushConstants)838 TEST_F(PipelineTest, OpenCLGeneratePushConstants) {
839 Pipeline p(PipelineType::kCompute);
840 p.SetName("my_pipeline");
841
842 Shader cs(kShaderTypeCompute);
843 cs.SetFormat(kShaderFormatOpenCLC);
844 p.AddShader(&cs, kShaderTypeCompute);
845 p.SetShaderEntryPoint(&cs, "my_main");
846
847 Pipeline::ShaderInfo::PushConstant pc1;
848 pc1.type = Pipeline::ShaderInfo::PushConstant::PushConstantType::kDimensions;
849 pc1.offset = 0;
850 pc1.size = 4;
851 p.GetShaders()[0].AddPushConstant(std::move(pc1));
852
853 Pipeline::ShaderInfo::PushConstant pc2;
854 pc2.type =
855 Pipeline::ShaderInfo::PushConstant::PushConstantType::kGlobalOffset;
856 pc2.offset = 16;
857 pc2.size = 12;
858 p.GetShaders()[0].AddPushConstant(std::move(pc2));
859
860 auto r = p.GenerateOpenCLPushConstants();
861 ASSERT_TRUE(r.IsSuccess());
862
863 const auto& buf = p.GetPushConstantBuffer();
864 EXPECT_EQ(28U, buf.buffer->GetSizeInBytes());
865
866 const uint32_t* bytes = buf.buffer->GetValues<uint32_t>();
867 EXPECT_EQ(3U, bytes[0]);
868 EXPECT_EQ(0U, bytes[4]);
869 EXPECT_EQ(0U, bytes[5]);
870 EXPECT_EQ(0U, bytes[6]);
871 }
872
TEST_F(PipelineTest,OpenCLPodPushConstants)873 TEST_F(PipelineTest, OpenCLPodPushConstants) {
874 Pipeline p(PipelineType::kCompute);
875 p.SetName("my_pipeline");
876
877 Shader cs(kShaderTypeCompute);
878 cs.SetFormat(kShaderFormatOpenCLC);
879 p.AddShader(&cs, kShaderTypeCompute);
880 p.SetShaderEntryPoint(&cs, "my_main");
881
882 // Descriptor map.
883 Pipeline::ShaderInfo::DescriptorMapEntry entry1;
884 entry1.kind =
885 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT;
886 entry1.descriptor_set = static_cast<uint32_t>(-1);
887 entry1.binding = static_cast<uint32_t>(-1);
888 entry1.arg_name = "arg_a";
889 entry1.arg_ordinal = 0;
890 entry1.pod_offset = 0;
891 entry1.pod_arg_size = 4;
892 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
893
894 Pipeline::ShaderInfo::DescriptorMapEntry entry2;
895 entry2.kind =
896 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT;
897 entry2.descriptor_set = static_cast<uint32_t>(-1);
898 entry2.binding = static_cast<uint32_t>(-1);
899 entry2.arg_name = "arg_b";
900 entry2.arg_ordinal = 1;
901 entry2.pod_offset = 4;
902 entry2.pod_arg_size = 1;
903 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
904
905 Pipeline::ShaderInfo::DescriptorMapEntry entry3;
906 entry3.kind =
907 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT;
908 entry3.descriptor_set = static_cast<uint32_t>(-1);
909 entry3.binding = static_cast<uint32_t>(-1);
910 entry3.arg_name = "arg_c";
911 entry3.arg_ordinal = 2;
912 entry3.pod_offset = 8;
913 entry3.pod_arg_size = 4;
914 p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry3));
915
916 // Set commands.
917 Value int_value;
918 int_value.SetIntValue(1);
919
920 TypeParser parser;
921 auto int_type = parser.Parse("R32_SINT");
922 auto int_fmt = MakeUnique<Format>(int_type.get());
923 auto char_type = parser.Parse("R8_SINT");
924 auto char_fmt = MakeUnique<Format>(char_type.get());
925
926 Pipeline::ArgSetInfo arg_info1;
927 arg_info1.name = "arg_a";
928 arg_info1.ordinal = 99;
929 arg_info1.fmt = int_fmt.get();
930 arg_info1.value = int_value;
931 p.SetArg(std::move(arg_info1));
932
933 Pipeline::ArgSetInfo arg_info2;
934 arg_info2.name = "arg_b";
935 arg_info2.ordinal = 99;
936 arg_info2.fmt = char_fmt.get();
937 arg_info2.value = int_value;
938 p.SetArg(std::move(arg_info2));
939
940 Pipeline::ArgSetInfo arg_info3;
941 arg_info3.name = "arg_c";
942 arg_info3.ordinal = 99;
943 arg_info3.fmt = int_fmt.get();
944 arg_info3.value = int_value;
945 p.SetArg(std::move(arg_info3));
946
947 auto r = p.GenerateOpenCLPodBuffers();
948 auto* buf = p.GetPushConstantBuffer().buffer;
949 EXPECT_NE(nullptr, buf);
950 EXPECT_EQ(12U, buf->GetSizeInBytes());
951
952 const uint32_t* ints = buf->GetValues<uint32_t>();
953 const uint8_t* bytes = buf->GetValues<uint8_t>();
954 EXPECT_EQ(1U, ints[0]);
955 EXPECT_EQ(1U, bytes[4]);
956 EXPECT_EQ(1U, ints[2]);
957 }
958
959 } // namespace amber
960