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 <algorithm>
18 #include <cstring>
19 #include <limits>
20 #include <set>
21 
22 #include "src/make_unique.h"
23 #include "src/type_parser.h"
24 
25 namespace amber {
26 namespace {
27 
28 const char* kDefaultColorBufferFormat = "B8G8R8A8_UNORM";
29 const char* kDefaultDepthBufferFormat = "D32_SFLOAT_S8_UINT";
30 
31 // OpenCL coordinates mode is bit 0
32 const uint32_t kOpenCLNormalizedCoordsBit = 1;
33 // OpenCL address mode bits are bits 1,2,3.
34 const uint32_t kOpenCLAddressModeBits = 0xe;
35 // OpenCL address mode bit values.
36 const uint32_t kOpenCLAddressModeNone = 0;
37 const uint32_t kOpenCLAddressModeClampToEdge = 2;
38 const uint32_t kOpenCLAddressModeClamp = 4;
39 const uint32_t kOpenCLAddressModeRepeat = 6;
40 const uint32_t kOpenCLAddressModeMirroredRepeat = 8;
41 // OpenCL filter mode bits.
42 const uint32_t kOpenCLFilterModeNearestBit = 0x10;
43 const uint32_t kOpenCLFilterModeLinearBit = 0x20;
44 
45 }  // namespace
46 
47 const char* Pipeline::kGeneratedColorBuffer = "framebuffer";
48 const char* Pipeline::kGeneratedDepthBuffer = "depth_buffer";
49 const char* Pipeline::kGeneratedPushConstantBuffer = "push_constant_buffer";
50 
ShaderInfo(Shader * shader,ShaderType type)51 Pipeline::ShaderInfo::ShaderInfo(Shader* shader, ShaderType type)
52     : shader_(shader),
53       shader_type_(type),
54       entry_point_("main"),
55       required_subgroup_size_setting_(RequiredSubgroupSizeSetting::kNotSet),
56       required_subgroup_size_(0),
57       varying_subgroup_size_(false),
58       require_full_subgroups_(false) {}
59 
60 Pipeline::ShaderInfo::ShaderInfo(const ShaderInfo&) = default;
61 
62 Pipeline::ShaderInfo::~ShaderInfo() = default;
63 
Pipeline(PipelineType type)64 Pipeline::Pipeline(PipelineType type) : pipeline_type_(type) {}
65 
66 Pipeline::~Pipeline() = default;
67 
Clone() const68 std::unique_ptr<Pipeline> Pipeline::Clone() const {
69   auto clone = MakeUnique<Pipeline>(pipeline_type_);
70   clone->shaders_ = shaders_;
71   clone->color_attachments_ = color_attachments_;
72   clone->vertex_buffers_ = vertex_buffers_;
73   clone->buffers_ = buffers_;
74   clone->depth_stencil_buffer_ = depth_stencil_buffer_;
75   clone->index_buffer_ = index_buffer_;
76   clone->fb_width_ = fb_width_;
77   clone->fb_height_ = fb_height_;
78   clone->set_arg_values_ = set_arg_values_;
79   clone->pipeline_data_ = pipeline_data_;
80 
81   if (!opencl_pod_buffers_.empty()) {
82     // Generate specific buffers for the clone.
83     clone->GenerateOpenCLPodBuffers();
84   }
85 
86   return clone;
87 }
88 
AddShader(Shader * shader,ShaderType shader_type)89 Result Pipeline::AddShader(Shader* shader, ShaderType shader_type) {
90   if (!shader)
91     return Result("shader can not be null when attached to pipeline");
92 
93   if (pipeline_type_ == PipelineType::kCompute &&
94       shader_type != kShaderTypeCompute) {
95     return Result("only compute shaders allowed in a compute pipeline");
96   }
97   if (pipeline_type_ == PipelineType::kGraphics &&
98       shader_type == kShaderTypeCompute) {
99     return Result("can not add a compute shader to a graphics pipeline");
100   }
101 
102   for (auto& info : shaders_) {
103     const auto* is = info.GetShader();
104     if (is == shader)
105       return Result("can not add duplicate shader to pipeline");
106     if (is->GetType() == shader_type) {
107       info.SetShader(shader);
108       return {};
109     }
110   }
111 
112   shaders_.emplace_back(shader, shader_type);
113   return {};
114 }
115 
SetShaderOptimizations(const Shader * shader,const std::vector<std::string> & opts)116 Result Pipeline::SetShaderOptimizations(const Shader* shader,
117                                         const std::vector<std::string>& opts) {
118   if (!shader)
119     return Result("invalid shader specified for optimizations");
120 
121   std::set<std::string> seen;
122   for (const auto& opt : opts) {
123     if (seen.count(opt) != 0)
124       return Result("duplicate optimization flag (" + opt + ") set on shader");
125 
126     seen.insert(opt);
127   }
128 
129   for (auto& info : shaders_) {
130     const auto* is = info.GetShader();
131     if (is == shader) {
132       info.SetShaderOptimizations(opts);
133       return {};
134     }
135   }
136 
137   return Result("unknown shader specified for optimizations: " +
138                 shader->GetName());
139 }
140 
SetShaderCompileOptions(const Shader * shader,const std::vector<std::string> & opts)141 Result Pipeline::SetShaderCompileOptions(const Shader* shader,
142                                          const std::vector<std::string>& opts) {
143   if (!shader)
144     return Result("invalid shader specified for compile options");
145 
146   for (auto& info : shaders_) {
147     const auto* is = info.GetShader();
148     if (is == shader) {
149       info.SetCompileOptions(opts);
150       return {};
151     }
152   }
153 
154   return Result("unknown shader specified for compile options: " +
155                 shader->GetName());
156 }
157 
SetShaderRequiredSubgroupSize(const Shader * shader,const ShaderInfo::RequiredSubgroupSizeSetting setting,const uint32_t size)158 Result Pipeline::SetShaderRequiredSubgroupSize(
159     const Shader* shader,
160     const ShaderInfo::RequiredSubgroupSizeSetting setting,
161     const uint32_t size) {
162   if (!shader)
163     return Result("invalid shader specified for  required subgroup size");
164 
165   for (auto& info : shaders_) {
166     const auto* is = info.GetShader();
167     if (is == shader) {
168       info.SetRequiredSubgroupSizeSetting(setting, size);
169       return {};
170     }
171   }
172 
173   return Result("unknown shader specified for required subgroup size: " +
174                 shader->GetName());
175 }
176 
SetShaderRequiredSubgroupSize(const Shader * shader,const uint32_t subgroupSize)177 Result Pipeline::SetShaderRequiredSubgroupSize(const Shader* shader,
178                                                const uint32_t subgroupSize) {
179   const bool isPow2 =
180       subgroupSize > 0 && (subgroupSize & (subgroupSize - 1)) == 0;
181   if (subgroupSize == 0 || subgroupSize > 128 || !isPow2) {
182     return Result("invalid required subgroup size " +
183                   std::to_string(subgroupSize) + " specified for shader name " +
184                   shader->GetName());
185   }
186   const ShaderInfo::RequiredSubgroupSizeSetting setting =
187       ShaderInfo::RequiredSubgroupSizeSetting::kSetToSpecificSize;
188   return SetShaderRequiredSubgroupSize(shader, setting, subgroupSize);
189 }
190 
SetShaderRequiredSubgroupSizeToMinimum(const Shader * shader)191 Result Pipeline::SetShaderRequiredSubgroupSizeToMinimum(const Shader* shader) {
192   const ShaderInfo::RequiredSubgroupSizeSetting subgroupSizeSetting =
193       ShaderInfo::RequiredSubgroupSizeSetting::kSetToMinimumSize;
194   return SetShaderRequiredSubgroupSize(shader, subgroupSizeSetting, 0);
195 }
196 
SetShaderRequiredSubgroupSizeToMaximum(const Shader * shader)197 Result Pipeline::SetShaderRequiredSubgroupSizeToMaximum(const Shader* shader) {
198   const ShaderInfo::RequiredSubgroupSizeSetting subgroupSizeSetting =
199       ShaderInfo::RequiredSubgroupSizeSetting::kSetToMaximumSize;
200   return SetShaderRequiredSubgroupSize(shader, subgroupSizeSetting, 0);
201 }
202 
SetShaderVaryingSubgroupSize(const Shader * shader,const bool isSet)203 Result Pipeline::SetShaderVaryingSubgroupSize(const Shader* shader,
204                                               const bool isSet) {
205   if (!shader)
206     return Result("invalid shader specified for varying subgroup size");
207 
208   for (auto& info : shaders_) {
209     const auto* is = info.GetShader();
210     if (is == shader) {
211       info.SetVaryingSubgroupSize(isSet);
212       return {};
213     }
214   }
215 
216   return Result("unknown shader specified for varying subgroup size: " +
217                 shader->GetName());
218 }
219 
SetShaderRequireFullSubgroups(const Shader * shader,const bool isSet)220 Result Pipeline::SetShaderRequireFullSubgroups(const Shader* shader,
221                                                const bool isSet) {
222   if (!shader)
223     return Result("invalid shader specified for optimizations");
224 
225   for (auto& info : shaders_) {
226     const auto* is = info.GetShader();
227     if (is == shader) {
228       info.SetRequireFullSubgroups(isSet);
229       return {};
230     }
231   }
232 
233   return Result("unknown shader specified for optimizations: " +
234                 shader->GetName());
235 }
236 
SetShaderEntryPoint(const Shader * shader,const std::string & name)237 Result Pipeline::SetShaderEntryPoint(const Shader* shader,
238                                      const std::string& name) {
239   if (!shader)
240     return Result("invalid shader specified for entry point");
241   if (name.empty())
242     return Result("entry point should not be blank");
243 
244   for (auto& info : shaders_) {
245     if (info.GetShader() == shader) {
246       if (info.GetEntryPoint() != "main")
247         return Result("multiple entry points given for the same shader");
248 
249       info.SetEntryPoint(name);
250       return {};
251     }
252   }
253 
254   return Result("unknown shader specified for entry point: " +
255                 shader->GetName());
256 }
257 
SetShaderType(const Shader * shader,ShaderType type)258 Result Pipeline::SetShaderType(const Shader* shader, ShaderType type) {
259   if (!shader)
260     return Result("invalid shader specified for shader type");
261 
262   for (auto& info : shaders_) {
263     if (info.GetShader() == shader) {
264       info.SetShaderType(type);
265       return {};
266     }
267   }
268 
269   return Result("unknown shader specified for shader type: " +
270                 shader->GetName());
271 }
272 
Validate() const273 Result Pipeline::Validate() const {
274   for (const auto& attachment : color_attachments_) {
275     if (attachment.buffer->ElementCount() !=
276         (fb_width_ << attachment.base_mip_level) *
277             (fb_height_ << attachment.base_mip_level)) {
278       return Result(
279           "shared framebuffer must have same size over all PIPELINES");
280     }
281   }
282 
283   if (depth_stencil_buffer_.buffer &&
284       depth_stencil_buffer_.buffer->ElementCount() != fb_width_ * fb_height_) {
285     return Result("shared depth buffer must have same size over all PIPELINES");
286   }
287 
288   for (auto& buf : GetBuffers()) {
289     if (buf.buffer->GetFormat() == nullptr) {
290       return Result("buffer (" + std::to_string(buf.descriptor_set) + ":" +
291                     std::to_string(buf.binding) + ") requires a format");
292     }
293   }
294 
295   if (pipeline_type_ == PipelineType::kGraphics)
296     return ValidateGraphics();
297 
298   return ValidateCompute();
299 }
300 
ValidateGraphics() const301 Result Pipeline::ValidateGraphics() const {
302   if (color_attachments_.empty())
303     return Result("PIPELINE missing color attachment");
304 
305   bool found_vertex = false;
306   for (const auto& info : shaders_) {
307     const auto* s = info.GetShader();
308     if (s->GetType() == kShaderTypeVertex) {
309       found_vertex = true;
310       break;
311     }
312   }
313 
314   if (!found_vertex)
315     return Result("graphics pipeline requires a vertex shader");
316 
317   for (const auto& att : color_attachments_) {
318     auto width = att.buffer->GetWidth();
319     auto height = att.buffer->GetHeight();
320     for (uint32_t level = 1; level < att.buffer->GetMipLevels(); level++) {
321       width >>= 1;
322       if (width == 0)
323         return Result("color attachment with " +
324                       std::to_string(att.buffer->GetMipLevels()) +
325                       " mip levels would have zero width for level " +
326                       std::to_string(level));
327       height >>= 1;
328       if (height == 0)
329         return Result("color attachment with " +
330                       std::to_string(att.buffer->GetMipLevels()) +
331                       " mip levels would have zero height for level " +
332                       std::to_string(level));
333     }
334   }
335 
336   return {};
337 }
338 
ValidateCompute() const339 Result Pipeline::ValidateCompute() const {
340   if (shaders_.empty())
341     return Result("compute pipeline requires a compute shader");
342 
343   return {};
344 }
345 
UpdateFramebufferSizes()346 void Pipeline::UpdateFramebufferSizes() {
347   uint32_t size = fb_width_ * fb_height_;
348   if (size == 0)
349     return;
350 
351   for (auto& attachment : color_attachments_) {
352     auto mip0_width = fb_width_ << attachment.base_mip_level;
353     auto mip0_height = fb_height_ << attachment.base_mip_level;
354     attachment.buffer->SetWidth(mip0_width);
355     attachment.buffer->SetHeight(mip0_height);
356     attachment.buffer->SetElementCount(mip0_width * mip0_height);
357   }
358 
359   if (depth_stencil_buffer_.buffer) {
360     depth_stencil_buffer_.buffer->SetWidth(fb_width_);
361     depth_stencil_buffer_.buffer->SetHeight(fb_height_);
362     depth_stencil_buffer_.buffer->SetElementCount(size);
363   }
364 }
365 
AddColorAttachment(Buffer * buf,uint32_t location,uint32_t base_mip_level)366 Result Pipeline::AddColorAttachment(Buffer* buf,
367                                     uint32_t location,
368                                     uint32_t base_mip_level) {
369   for (const auto& attachment : color_attachments_) {
370     if (attachment.location == location)
371       return Result("can not bind two color buffers to the same LOCATION");
372     if (attachment.buffer == buf)
373       return Result("color buffer may only be bound to a PIPELINE once");
374   }
375 
376   color_attachments_.push_back(BufferInfo{buf});
377 
378   auto& info = color_attachments_.back();
379   info.location = location;
380   info.type = BufferType::kColor;
381   info.base_mip_level = base_mip_level;
382   auto mip0_width = fb_width_ << base_mip_level;
383   auto mip0_height = fb_height_ << base_mip_level;
384   buf->SetWidth(mip0_width);
385   buf->SetHeight(mip0_height);
386   buf->SetElementCount(mip0_width * mip0_height);
387 
388   return {};
389 }
390 
AddResolveTarget(Buffer * buf)391 Result Pipeline::AddResolveTarget(Buffer* buf) {
392   resolve_targets_.push_back(BufferInfo{buf});
393 
394   auto& info = resolve_targets_.back();
395   info.type = BufferType::kResolve;
396   buf->SetWidth(fb_width_);
397   buf->SetHeight(fb_height_);
398   buf->SetElementCount(fb_width_ * fb_height_);
399 
400   return {};
401 }
402 
GetLocationForColorAttachment(Buffer * buf,uint32_t * loc) const403 Result Pipeline::GetLocationForColorAttachment(Buffer* buf,
404                                                uint32_t* loc) const {
405   for (const auto& info : color_attachments_) {
406     if (info.buffer == buf) {
407       *loc = info.location;
408       return {};
409     }
410   }
411   return Result("Unable to find requested buffer");
412 }
413 
SetDepthStencilBuffer(Buffer * buf)414 Result Pipeline::SetDepthStencilBuffer(Buffer* buf) {
415   if (depth_stencil_buffer_.buffer != nullptr)
416     return Result("can only bind one depth/stencil buffer in a PIPELINE");
417 
418   depth_stencil_buffer_.buffer = buf;
419   depth_stencil_buffer_.type = BufferType::kDepthStencil;
420 
421   buf->SetWidth(fb_width_);
422   buf->SetHeight(fb_height_);
423   buf->SetElementCount(fb_width_ * fb_height_);
424   return {};
425 }
426 
SetIndexBuffer(Buffer * buf)427 Result Pipeline::SetIndexBuffer(Buffer* buf) {
428   if (index_buffer_ != nullptr)
429     return Result("can only bind one INDEX_DATA buffer in a pipeline");
430 
431   index_buffer_ = buf;
432   return {};
433 }
434 
AddVertexBuffer(Buffer * buf,uint32_t location,InputRate rate,Format * format,uint32_t offset,uint32_t stride)435 Result Pipeline::AddVertexBuffer(Buffer* buf,
436                                  uint32_t location,
437                                  InputRate rate,
438                                  Format* format,
439                                  uint32_t offset,
440                                  uint32_t stride) {
441   for (const auto& vtex : vertex_buffers_) {
442     if (vtex.location == location)
443       return Result("can not bind two vertex buffers to the same LOCATION");
444   }
445 
446   vertex_buffers_.push_back(BufferInfo{buf});
447   vertex_buffers_.back().location = location;
448   vertex_buffers_.back().type = BufferType::kVertex;
449   vertex_buffers_.back().input_rate = rate;
450   vertex_buffers_.back().format = format;
451   vertex_buffers_.back().offset = offset;
452   vertex_buffers_.back().stride = stride;
453   return {};
454 }
455 
SetPushConstantBuffer(Buffer * buf)456 Result Pipeline::SetPushConstantBuffer(Buffer* buf) {
457   if (push_constant_buffer_.buffer != nullptr)
458     return Result("can only bind one push constant buffer in a PIPELINE");
459 
460   push_constant_buffer_.buffer = buf;
461   push_constant_buffer_.type = BufferType::kPushConstant;
462   return {};
463 }
464 
CreatePushConstantBuffer()465 Result Pipeline::CreatePushConstantBuffer() {
466   if (push_constant_buffer_.buffer != nullptr)
467     return Result("can only bind one push constant buffer in a PIPELINE");
468 
469   TypeParser parser;
470   auto type = parser.Parse("R8_UINT");
471   auto fmt = MakeUnique<Format>(type.get());
472 
473   std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
474   buf->SetName(kGeneratedPushConstantBuffer);
475   buf->SetFormat(fmt.get());
476 
477   push_constant_buffer_.buffer = buf.get();
478   push_constant_buffer_.type = BufferType::kPushConstant;
479 
480   formats_.push_back(std::move(fmt));
481   types_.push_back(std::move(type));
482   opencl_push_constants_ = std::move(buf);
483 
484   return {};
485 }
486 
GenerateDefaultColorAttachmentBuffer()487 std::unique_ptr<Buffer> Pipeline::GenerateDefaultColorAttachmentBuffer() {
488   TypeParser parser;
489   auto type = parser.Parse(kDefaultColorBufferFormat);
490   auto fmt = MakeUnique<Format>(type.get());
491 
492   std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
493   buf->SetName(kGeneratedColorBuffer);
494   buf->SetFormat(fmt.get());
495 
496   formats_.push_back(std::move(fmt));
497   types_.push_back(std::move(type));
498   return buf;
499 }
500 
501 std::unique_ptr<Buffer>
GenerateDefaultDepthStencilAttachmentBuffer()502 Pipeline::GenerateDefaultDepthStencilAttachmentBuffer() {
503   TypeParser parser;
504   auto type = parser.Parse(kDefaultDepthBufferFormat);
505   auto fmt = MakeUnique<Format>(type.get());
506 
507   std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
508   buf->SetName(kGeneratedDepthBuffer);
509   buf->SetFormat(fmt.get());
510 
511   formats_.push_back(std::move(fmt));
512   types_.push_back(std::move(type));
513   return buf;
514 }
515 
GetBufferForBinding(uint32_t descriptor_set,uint32_t binding) const516 Buffer* Pipeline::GetBufferForBinding(uint32_t descriptor_set,
517                                       uint32_t binding) const {
518   for (const auto& info : buffers_) {
519     if (info.descriptor_set == descriptor_set && info.binding == binding)
520       return info.buffer;
521   }
522   return nullptr;
523 }
524 
AddBuffer(Buffer * buf,BufferType type,uint32_t descriptor_set,uint32_t binding,uint32_t base_mip_level,uint32_t dynamic_offset,uint64_t descriptor_offset,uint64_t descriptor_range)525 void Pipeline::AddBuffer(Buffer* buf,
526                          BufferType type,
527                          uint32_t descriptor_set,
528                          uint32_t binding,
529                          uint32_t base_mip_level,
530                          uint32_t dynamic_offset,
531                          uint64_t descriptor_offset,
532                          uint64_t descriptor_range) {
533   buffers_.push_back(BufferInfo{buf});
534 
535   auto& info = buffers_.back();
536   info.descriptor_set = descriptor_set;
537   info.binding = binding;
538   info.type = type;
539   info.base_mip_level = base_mip_level;
540   info.dynamic_offset = dynamic_offset;
541   info.sampler = buf->GetSampler();
542   info.descriptor_offset = descriptor_offset;
543   info.descriptor_range = descriptor_range;
544 }
545 
AddBuffer(Buffer * buf,BufferType type,const std::string & arg_name)546 void Pipeline::AddBuffer(Buffer* buf,
547                          BufferType type,
548                          const std::string& arg_name) {
549   // If this buffer binding already exists, overwrite with the new buffer.
550   for (auto& info : buffers_) {
551     if (info.arg_name == arg_name) {
552       info.buffer = buf;
553       return;
554     }
555   }
556 
557   buffers_.push_back(BufferInfo{buf});
558 
559   auto& info = buffers_.back();
560   info.type = type;
561   info.arg_name = arg_name;
562   info.descriptor_set = std::numeric_limits<uint32_t>::max();
563   info.binding = std::numeric_limits<uint32_t>::max();
564   info.arg_no = std::numeric_limits<uint32_t>::max();
565   info.base_mip_level = 0;
566   info.dynamic_offset = 0;
567 }
568 
AddBuffer(Buffer * buf,BufferType type,uint32_t arg_no)569 void Pipeline::AddBuffer(Buffer* buf, BufferType type, uint32_t arg_no) {
570   // If this buffer binding already exists, overwrite with the new buffer.
571   for (auto& info : buffers_) {
572     if (info.arg_no == arg_no) {
573       info.buffer = buf;
574       return;
575     }
576   }
577 
578   buffers_.push_back(BufferInfo{buf});
579 
580   auto& info = buffers_.back();
581   info.type = type;
582   info.arg_no = arg_no;
583   info.descriptor_set = std::numeric_limits<uint32_t>::max();
584   info.binding = std::numeric_limits<uint32_t>::max();
585   info.base_mip_level = 0;
586   info.dynamic_offset = 0;
587 }
588 
ClearBuffers(uint32_t descriptor_set,uint32_t binding)589 void Pipeline::ClearBuffers(uint32_t descriptor_set, uint32_t binding) {
590   buffers_.erase(
591       std::remove_if(buffers_.begin(), buffers_.end(),
592                      [descriptor_set, binding](BufferInfo& info) -> bool {
593                        return (info.descriptor_set == descriptor_set &&
594                                info.binding == binding);
595                      }),
596       buffers_.end());
597 }
598 
AddSampler(Sampler * sampler,uint32_t descriptor_set,uint32_t binding)599 void Pipeline::AddSampler(Sampler* sampler,
600                           uint32_t descriptor_set,
601                           uint32_t binding) {
602   samplers_.push_back(SamplerInfo{sampler});
603 
604   auto& info = samplers_.back();
605   info.descriptor_set = descriptor_set;
606   info.binding = binding;
607   info.mask = std::numeric_limits<uint32_t>::max();
608 }
609 
AddSampler(Sampler * sampler,const std::string & arg_name)610 void Pipeline::AddSampler(Sampler* sampler, const std::string& arg_name) {
611   for (auto& info : samplers_) {
612     if (info.arg_name == arg_name) {
613       info.sampler = sampler;
614       return;
615     }
616   }
617 
618   samplers_.push_back(SamplerInfo{sampler});
619 
620   auto& info = samplers_.back();
621   info.arg_name = arg_name;
622   info.descriptor_set = std::numeric_limits<uint32_t>::max();
623   info.binding = std::numeric_limits<uint32_t>::max();
624   info.arg_no = std::numeric_limits<uint32_t>::max();
625   info.mask = std::numeric_limits<uint32_t>::max();
626 }
627 
AddSampler(Sampler * sampler,uint32_t arg_no)628 void Pipeline::AddSampler(Sampler* sampler, uint32_t arg_no) {
629   for (auto& info : samplers_) {
630     if (info.arg_no == arg_no) {
631       info.sampler = sampler;
632       return;
633     }
634   }
635 
636   samplers_.push_back(SamplerInfo{sampler});
637 
638   auto& info = samplers_.back();
639   info.arg_no = arg_no;
640   info.descriptor_set = std::numeric_limits<uint32_t>::max();
641   info.binding = std::numeric_limits<uint32_t>::max();
642   info.mask = std::numeric_limits<uint32_t>::max();
643 }
644 
AddSampler(uint32_t mask,uint32_t descriptor_set,uint32_t binding)645 void Pipeline::AddSampler(uint32_t mask,
646                           uint32_t descriptor_set,
647                           uint32_t binding) {
648   samplers_.push_back(SamplerInfo{nullptr});
649 
650   auto& info = samplers_.back();
651   info.arg_name = "";
652   info.arg_no = std::numeric_limits<uint32_t>::max();
653   info.mask = mask;
654   info.descriptor_set = descriptor_set;
655   info.binding = binding;
656 }
657 
ClearSamplers(uint32_t descriptor_set,uint32_t binding)658 void Pipeline::ClearSamplers(uint32_t descriptor_set, uint32_t binding) {
659   samplers_.erase(
660       std::remove_if(samplers_.begin(), samplers_.end(),
661                      [descriptor_set, binding](SamplerInfo& info) -> bool {
662                        return (info.descriptor_set == descriptor_set &&
663                                info.binding == binding);
664                      }),
665       samplers_.end());
666 }
667 
UpdateOpenCLBufferBindings()668 Result Pipeline::UpdateOpenCLBufferBindings() {
669   if (!IsCompute() || GetShaders().empty() ||
670       GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
671     return {};
672   }
673 
674   const auto& shader_info = GetShaders()[0];
675   const auto& descriptor_map = shader_info.GetDescriptorMap();
676   if (descriptor_map.empty())
677     return {};
678 
679   const auto iter = descriptor_map.find(shader_info.GetEntryPoint());
680   if (iter == descriptor_map.end())
681     return {};
682 
683   for (auto& info : samplers_) {
684     if (info.descriptor_set == std::numeric_limits<uint32_t>::max() &&
685         info.binding == std::numeric_limits<uint32_t>::max()) {
686       for (const auto& entry : iter->second) {
687         if (entry.arg_name == info.arg_name ||
688             entry.arg_ordinal == info.arg_no) {
689           if (entry.kind !=
690               Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SAMPLER) {
691             return Result("Sampler bound to non-sampler kernel arg");
692           }
693           info.descriptor_set = entry.descriptor_set;
694           info.binding = entry.binding;
695         }
696       }
697     }
698   }
699 
700   for (auto& info : buffers_) {
701     if (info.descriptor_set == std::numeric_limits<uint32_t>::max() &&
702         info.binding == std::numeric_limits<uint32_t>::max()) {
703       for (const auto& entry : iter->second) {
704         if (entry.arg_name == info.arg_name ||
705             entry.arg_ordinal == info.arg_no) {
706           // Buffer storage class consistency checks.
707           if (info.type == BufferType::kUnknown) {
708             // Set the appropriate buffer type.
709             switch (entry.kind) {
710               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO:
711               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO:
712                 info.type = BufferType::kUniform;
713                 break;
714               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO:
715               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD:
716                 info.type = BufferType::kStorage;
717                 break;
718               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE:
719                 info.type = BufferType::kSampledImage;
720                 break;
721               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE:
722                 info.type = BufferType::kStorageImage;
723                 break;
724               default:
725                 return Result("Unhandled buffer type for OPENCL-C shader");
726             }
727           } else if (info.type == BufferType::kUniform) {
728             if (entry.kind !=
729                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO &&
730                 entry.kind !=
731                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO) {
732               return Result("Buffer " + info.buffer->GetName() +
733                             " must be a uniform binding");
734             }
735           } else if (info.type == BufferType::kStorage) {
736             if (entry.kind !=
737                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO &&
738                 entry.kind !=
739                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD) {
740               return Result("Buffer " + info.buffer->GetName() +
741                             " must be a storage binding");
742             }
743           } else if (info.type == BufferType::kSampledImage) {
744             if (entry.kind !=
745                 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE) {
746               return Result("Buffer " + info.buffer->GetName() +
747                             " must be a read-only image binding");
748             }
749           } else if (info.type == BufferType::kStorageImage) {
750             if (entry.kind !=
751                 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE) {
752               return Result("Buffer " + info.buffer->GetName() +
753                             " must be a write-only image binding");
754             }
755           } else {
756             return Result("Unhandled buffer type for OPENCL-C shader");
757           }
758           info.descriptor_set = entry.descriptor_set;
759           info.binding = entry.binding;
760         }
761       }
762     }
763   }
764 
765   return {};
766 }
767 
GenerateOpenCLPodBuffers()768 Result Pipeline::GenerateOpenCLPodBuffers() {
769   if (!IsCompute() || GetShaders().empty() ||
770       GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
771     return {};
772   }
773 
774   const auto& shader_info = GetShaders()[0];
775   const auto& descriptor_map = shader_info.GetDescriptorMap();
776   if (descriptor_map.empty())
777     return {};
778 
779   const auto iter = descriptor_map.find(shader_info.GetEntryPoint());
780   if (iter == descriptor_map.end())
781     return {};
782 
783   // For each SET command, do the following:
784   // 1. Find the descriptor map entry for that argument.
785   // 2. Find or create the buffer for the descriptor set and binding pair.
786   // 3. Write the data for the SET command at the right offset.
787   for (const auto& arg_info : SetArgValues()) {
788     uint32_t descriptor_set = std::numeric_limits<uint32_t>::max();
789     uint32_t binding = std::numeric_limits<uint32_t>::max();
790     uint32_t offset = 0;
791     uint32_t arg_size = 0;
792     bool uses_name = !arg_info.name.empty();
793     Pipeline::ShaderInfo::DescriptorMapEntry::Kind kind =
794         Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
795     for (const auto& entry : iter->second) {
796       if (entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD &&
797           entry.kind !=
798               Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO &&
799           entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::
800                             POD_PUSHCONSTANT) {
801         continue;
802       }
803 
804       // Found the right entry.
805       if ((uses_name && entry.arg_name == arg_info.name) ||
806           entry.arg_ordinal == arg_info.ordinal) {
807         descriptor_set = entry.descriptor_set;
808         binding = entry.binding;
809         offset = entry.pod_offset;
810         arg_size = entry.pod_arg_size;
811         kind = entry.kind;
812         break;
813       }
814     }
815 
816     Buffer* buffer = nullptr;
817     if (kind ==
818         Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT) {
819       if (GetPushConstantBuffer().buffer == nullptr) {
820         auto r = CreatePushConstantBuffer();
821         if (!r.IsSuccess())
822           return r;
823       }
824       buffer = GetPushConstantBuffer().buffer;
825     } else {
826       if (descriptor_set == std::numeric_limits<uint32_t>::max() ||
827           binding == std::numeric_limits<uint32_t>::max()) {
828         std::string message =
829             "could not find descriptor map entry for SET command: kernel " +
830             shader_info.GetEntryPoint();
831         if (uses_name) {
832           message += ", name " + arg_info.name;
833         } else {
834           message += ", number " + std::to_string(arg_info.ordinal);
835         }
836         return Result(message);
837       }
838 
839       auto buf_iter = opencl_pod_buffer_map_.lower_bound(
840           std::make_pair(descriptor_set, binding));
841       if (buf_iter == opencl_pod_buffer_map_.end() ||
842           buf_iter->first.first != descriptor_set ||
843           buf_iter->first.second != binding) {
844         // Ensure no buffer was previously bound for this descriptor set and
845         // binding pair.
846         for (const auto& buf_info : GetBuffers()) {
847           if (buf_info.descriptor_set == descriptor_set &&
848               buf_info.binding == binding) {
849             return Result("previously bound buffer " +
850                           buf_info.buffer->GetName() +
851                           " to PoD args at descriptor set " +
852                           std::to_string(descriptor_set) + " binding " +
853                           std::to_string(binding));
854           }
855         }
856 
857         // Add a new buffer for this descriptor set and binding.
858         opencl_pod_buffers_.push_back(MakeUnique<Buffer>());
859         buffer = opencl_pod_buffers_.back().get();
860         auto buffer_type =
861             kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD
862                 ? BufferType::kStorage
863                 : BufferType::kUniform;
864 
865         // Use an 8-bit type because all the data in the descriptor map is
866         // byte-based and it simplifies the logic for sizing below.
867         TypeParser parser;
868         auto type = parser.Parse("R8_UINT");
869         auto fmt = MakeUnique<Format>(type.get());
870         buffer->SetFormat(fmt.get());
871         formats_.push_back(std::move(fmt));
872         types_.push_back(std::move(type));
873 
874         buffer->SetName(GetName() + "_pod_buffer_" +
875                         std::to_string(descriptor_set) + "_" +
876                         std::to_string(binding));
877         opencl_pod_buffer_map_.insert(
878             buf_iter,
879             std::make_pair(std::make_pair(descriptor_set, binding), buffer));
880         AddBuffer(buffer, buffer_type, descriptor_set, binding, 0, 0, 0, ~0ULL);
881       } else {
882         buffer = buf_iter->second;
883       }
884 
885       // Resize if necessary.
886       if (buffer->ValueCount() < offset + arg_size) {
887         buffer->SetSizeInElements(offset + arg_size);
888       }
889 
890       // Check the data size.
891       if (arg_size != arg_info.fmt->SizeInBytes()) {
892         std::string message = "SET command uses incorrect data size: kernel " +
893                               shader_info.GetEntryPoint();
894         if (uses_name) {
895           message += ", name " + arg_info.name;
896         } else {
897           message += ", number " + std::to_string(arg_info.ordinal);
898         }
899         return Result(message);
900       }
901     }
902 
903     // Convert the argument value into bytes. Currently, only scalar arguments
904     // are supported.
905     const auto arg_byte_size = arg_info.fmt->SizeInBytes();
906     std::vector<Value> data_bytes;
907     for (uint32_t i = 0; i < arg_byte_size; ++i) {
908       Value v;
909       if (arg_info.value.IsFloat()) {
910         if (arg_byte_size == sizeof(double)) {
911           union {
912             uint64_t u;
913             double d;
914           } u;
915           u.d = arg_info.value.AsDouble();
916           v.SetIntValue((u.u >> (i * 8)) & 0xff);
917         } else {
918           union {
919             uint32_t u;
920             float f;
921           } u;
922           u.f = arg_info.value.AsFloat();
923           v.SetIntValue((u.u >> (i * 8)) & 0xff);
924         }
925       } else {
926         v.SetIntValue((arg_info.value.AsUint64() >> (i * 8)) & 0xff);
927       }
928       data_bytes.push_back(v);
929     }
930     Result r = buffer->SetDataWithOffset(data_bytes, offset);
931     if (!r.IsSuccess())
932       return r;
933   }
934 
935   return {};
936 }
937 
GenerateOpenCLLiteralSamplers()938 Result Pipeline::GenerateOpenCLLiteralSamplers() {
939   for (auto& info : samplers_) {
940     if (info.sampler || info.mask == std::numeric_limits<uint32_t>::max())
941       continue;
942 
943     auto literal_sampler = MakeUnique<Sampler>();
944     literal_sampler->SetName("literal." + std::to_string(info.descriptor_set) +
945                              "." + std::to_string(info.binding));
946 
947     // The values for addressing modes, filtering modes and coordinate
948     // normalization are all defined in the OpenCL header.
949 
950     literal_sampler->SetNormalizedCoords(info.mask &
951                                          kOpenCLNormalizedCoordsBit);
952 
953     uint32_t addressing_bits = info.mask & kOpenCLAddressModeBits;
954     AddressMode addressing_mode = AddressMode::kUnknown;
955     if (addressing_bits == kOpenCLAddressModeNone ||
956         addressing_bits == kOpenCLAddressModeClampToEdge) {
957       // CLK_ADDRESS_NONE
958       // CLK_ADDERSS_CLAMP_TO_EDGE
959       addressing_mode = AddressMode::kClampToEdge;
960     } else if (addressing_bits == kOpenCLAddressModeClamp) {
961       // CLK_ADDRESS_CLAMP
962       addressing_mode = AddressMode::kClampToBorder;
963     } else if (addressing_bits == kOpenCLAddressModeRepeat) {
964       // CLK_ADDRESS_REPEAT
965       addressing_mode = AddressMode::kRepeat;
966     } else if (addressing_bits == kOpenCLAddressModeMirroredRepeat) {
967       // CLK_ADDRESS_MIRRORED_REPEAT
968       addressing_mode = AddressMode::kMirroredRepeat;
969     }
970     literal_sampler->SetAddressModeU(addressing_mode);
971     literal_sampler->SetAddressModeV(addressing_mode);
972     // TODO(alan-baker): If this is used with an arrayed image then W should use
973     // kClampToEdge always, but this information is not currently available.
974     literal_sampler->SetAddressModeW(addressing_mode);
975 
976     // Next bit is filtering mode.
977     FilterType filtering_mode = FilterType::kUnknown;
978     if (info.mask & kOpenCLFilterModeNearestBit) {
979       filtering_mode = FilterType::kNearest;
980     } else if (info.mask & kOpenCLFilterModeLinearBit) {
981       filtering_mode = FilterType::kLinear;
982     }
983     literal_sampler->SetMagFilter(filtering_mode);
984     literal_sampler->SetMinFilter(filtering_mode);
985 
986     // TODO(alan-baker): OpenCL wants the border color to be based on image
987     // channel orders which aren't accessible.
988 
989     // clspv never generates multiple MIPMAP levels.
990     literal_sampler->SetMinLOD(0.0f);
991     literal_sampler->SetMaxLOD(0.0f);
992 
993     opencl_literal_samplers_.push_back(std::move(literal_sampler));
994     info.sampler = opencl_literal_samplers_.back().get();
995   }
996 
997   return {};
998 }
999 
GenerateOpenCLPushConstants()1000 Result Pipeline::GenerateOpenCLPushConstants() {
1001   if (!IsCompute() || GetShaders().empty() ||
1002       GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
1003     return {};
1004   }
1005 
1006   const auto& shader_info = GetShaders()[0];
1007   if (shader_info.GetPushConstants().empty())
1008     return {};
1009 
1010   Result r = CreatePushConstantBuffer();
1011   if (!r.IsSuccess())
1012     return r;
1013 
1014   auto* buf = GetPushConstantBuffer().buffer;
1015   assert(buf);
1016 
1017   // Determine size and contents of the push constant buffer.
1018   for (const auto& pc : shader_info.GetPushConstants()) {
1019     assert(pc.size % sizeof(uint32_t) == 0);
1020     assert(pc.offset % sizeof(uint32_t) == 0);
1021 
1022     if (buf->GetSizeInBytes() < pc.offset + pc.size)
1023       buf->SetSizeInBytes(pc.offset + pc.size);
1024 
1025     std::vector<uint32_t> bytes(pc.size / sizeof(uint32_t));
1026     uint32_t base = 0;
1027     switch (pc.type) {
1028       case Pipeline::ShaderInfo::PushConstant::PushConstantType::kDimensions:
1029         // All compute kernel launches are 3D.
1030         bytes[base] = 3;
1031         break;
1032       case Pipeline::ShaderInfo::PushConstant::PushConstantType::kGlobalOffset:
1033         // Global offsets are not currently supported.
1034         bytes[base] = 0;
1035         bytes[base + 1] = 0;
1036         bytes[base + 2] = 0;
1037         break;
1038       case Pipeline::ShaderInfo::PushConstant::PushConstantType::kRegionOffset:
1039         // Region offsets are not currently supported.
1040         bytes[base] = 0;
1041         bytes[base + 1] = 0;
1042         bytes[base + 2] = 0;
1043         break;
1044     }
1045     memcpy(buf->ValuePtr()->data() + pc.offset, bytes.data(),
1046            bytes.size() * sizeof(uint32_t));
1047   }
1048 
1049   return {};
1050 }
1051 
1052 }  // namespace amber
1053