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