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