• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AddResolveTarget(Buffer * buf)392 Result Pipeline::AddResolveTarget(Buffer* buf) {
393   resolve_targets_.push_back(BufferInfo{buf});
394 
395   auto& info = resolve_targets_.back();
396   info.type = BufferType::kResolve;
397   buf->SetWidth(fb_width_);
398   buf->SetHeight(fb_height_);
399   buf->SetElementCount(fb_width_ * fb_height_);
400 
401   return {};
402 }
403 
GetLocationForColorAttachment(Buffer * buf,uint32_t * loc) const404 Result Pipeline::GetLocationForColorAttachment(Buffer* buf,
405                                                uint32_t* loc) const {
406   for (const auto& info : color_attachments_) {
407     if (info.buffer == buf) {
408       *loc = info.location;
409       return {};
410     }
411   }
412   return Result("Unable to find requested buffer");
413 }
414 
SetDepthStencilBuffer(Buffer * buf)415 Result Pipeline::SetDepthStencilBuffer(Buffer* buf) {
416   if (depth_stencil_buffer_.buffer != nullptr)
417     return Result("can only bind one depth/stencil buffer in a PIPELINE");
418 
419   depth_stencil_buffer_.buffer = buf;
420   depth_stencil_buffer_.type = BufferType::kDepthStencil;
421 
422   buf->SetWidth(fb_width_);
423   buf->SetHeight(fb_height_);
424   buf->SetElementCount(fb_width_ * fb_height_);
425   return {};
426 }
427 
SetIndexBuffer(Buffer * buf)428 Result Pipeline::SetIndexBuffer(Buffer* buf) {
429   if (index_buffer_ != nullptr)
430     return Result("can only bind one INDEX_DATA buffer in a pipeline");
431 
432   index_buffer_ = buf;
433   return {};
434 }
435 
AddVertexBuffer(Buffer * buf,uint32_t location,InputRate rate,Format * format,uint32_t offset,uint32_t stride)436 Result Pipeline::AddVertexBuffer(Buffer* buf,
437                                  uint32_t location,
438                                  InputRate rate,
439                                  Format* format,
440                                  uint32_t offset,
441                                  uint32_t stride) {
442   for (const auto& vtex : vertex_buffers_) {
443     if (vtex.location == location)
444       return Result("can not bind two vertex buffers to the same LOCATION");
445   }
446 
447   vertex_buffers_.push_back(BufferInfo{buf});
448   vertex_buffers_.back().location = location;
449   vertex_buffers_.back().type = BufferType::kVertex;
450   vertex_buffers_.back().input_rate = rate;
451   vertex_buffers_.back().format = format;
452   vertex_buffers_.back().offset = offset;
453   vertex_buffers_.back().stride = stride;
454   return {};
455 }
456 
SetPushConstantBuffer(Buffer * buf)457 Result Pipeline::SetPushConstantBuffer(Buffer* buf) {
458   if (push_constant_buffer_.buffer != nullptr)
459     return Result("can only bind one push constant buffer in a PIPELINE");
460 
461   push_constant_buffer_.buffer = buf;
462   push_constant_buffer_.type = BufferType::kPushConstant;
463   return {};
464 }
465 
CreatePushConstantBuffer()466 Result Pipeline::CreatePushConstantBuffer() {
467   if (push_constant_buffer_.buffer != nullptr)
468     return Result("can only bind one push constant buffer in a PIPELINE");
469 
470   TypeParser parser;
471   auto type = parser.Parse("R8_UINT");
472   auto fmt = MakeUnique<Format>(type.get());
473 
474   std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
475   buf->SetName(kGeneratedPushConstantBuffer);
476   buf->SetFormat(fmt.get());
477 
478   push_constant_buffer_.buffer = buf.get();
479   push_constant_buffer_.type = BufferType::kPushConstant;
480 
481   formats_.push_back(std::move(fmt));
482   types_.push_back(std::move(type));
483   opencl_push_constants_ = std::move(buf);
484 
485   return {};
486 }
487 
GenerateDefaultColorAttachmentBuffer()488 std::unique_ptr<Buffer> Pipeline::GenerateDefaultColorAttachmentBuffer() {
489   TypeParser parser;
490   auto type = parser.Parse(kDefaultColorBufferFormat);
491   auto fmt = MakeUnique<Format>(type.get());
492 
493   std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
494   buf->SetName(kGeneratedColorBuffer);
495   buf->SetFormat(fmt.get());
496 
497   formats_.push_back(std::move(fmt));
498   types_.push_back(std::move(type));
499   return buf;
500 }
501 
502 std::unique_ptr<Buffer>
GenerateDefaultDepthStencilAttachmentBuffer()503 Pipeline::GenerateDefaultDepthStencilAttachmentBuffer() {
504   TypeParser parser;
505   auto type = parser.Parse(kDefaultDepthBufferFormat);
506   auto fmt = MakeUnique<Format>(type.get());
507 
508   std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
509   buf->SetName(kGeneratedDepthBuffer);
510   buf->SetFormat(fmt.get());
511 
512   formats_.push_back(std::move(fmt));
513   types_.push_back(std::move(type));
514   return buf;
515 }
516 
GetBufferForBinding(uint32_t descriptor_set,uint32_t binding) const517 Buffer* Pipeline::GetBufferForBinding(uint32_t descriptor_set,
518                                       uint32_t binding) const {
519   for (const auto& info : buffers_) {
520     if (info.descriptor_set == descriptor_set && info.binding == binding)
521       return info.buffer;
522   }
523   return nullptr;
524 }
525 
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)526 void Pipeline::AddBuffer(Buffer* buf,
527                          BufferType type,
528                          uint32_t descriptor_set,
529                          uint32_t binding,
530                          uint32_t base_mip_level,
531                          uint32_t dynamic_offset,
532                          uint64_t descriptor_offset,
533                          uint64_t descriptor_range) {
534   buffers_.push_back(BufferInfo{buf});
535 
536   auto& info = buffers_.back();
537   info.descriptor_set = descriptor_set;
538   info.binding = binding;
539   info.type = type;
540   info.base_mip_level = base_mip_level;
541   info.dynamic_offset = dynamic_offset;
542   info.sampler = buf->GetSampler();
543   info.descriptor_offset = descriptor_offset;
544   info.descriptor_range = descriptor_range;
545 }
546 
AddBuffer(Buffer * buf,BufferType type,const std::string & arg_name)547 void Pipeline::AddBuffer(Buffer* buf,
548                          BufferType type,
549                          const std::string& arg_name) {
550   // If this buffer binding already exists, overwrite with the new buffer.
551   for (auto& info : buffers_) {
552     if (info.arg_name == arg_name) {
553       info.buffer = buf;
554       return;
555     }
556   }
557 
558   buffers_.push_back(BufferInfo{buf});
559 
560   auto& info = buffers_.back();
561   info.type = type;
562   info.arg_name = arg_name;
563   info.descriptor_set = std::numeric_limits<uint32_t>::max();
564   info.binding = std::numeric_limits<uint32_t>::max();
565   info.arg_no = std::numeric_limits<uint32_t>::max();
566   info.base_mip_level = 0;
567   info.dynamic_offset = 0;
568 }
569 
AddBuffer(Buffer * buf,BufferType type,uint32_t arg_no)570 void Pipeline::AddBuffer(Buffer* buf, BufferType type, uint32_t arg_no) {
571   // If this buffer binding already exists, overwrite with the new buffer.
572   for (auto& info : buffers_) {
573     if (info.arg_no == arg_no) {
574       info.buffer = buf;
575       return;
576     }
577   }
578 
579   buffers_.push_back(BufferInfo{buf});
580 
581   auto& info = buffers_.back();
582   info.type = type;
583   info.arg_no = arg_no;
584   info.descriptor_set = std::numeric_limits<uint32_t>::max();
585   info.binding = std::numeric_limits<uint32_t>::max();
586   info.base_mip_level = 0;
587   info.dynamic_offset = 0;
588 }
589 
ClearBuffers(uint32_t descriptor_set,uint32_t binding)590 void Pipeline::ClearBuffers(uint32_t descriptor_set, uint32_t binding) {
591   buffers_.erase(
592       std::remove_if(buffers_.begin(), buffers_.end(),
593                      [descriptor_set, binding](BufferInfo& info) -> bool {
594                        return (info.descriptor_set == descriptor_set &&
595                                info.binding == binding);
596                      }),
597       buffers_.end());
598 }
599 
AddSampler(Sampler * sampler,uint32_t descriptor_set,uint32_t binding)600 void Pipeline::AddSampler(Sampler* sampler,
601                           uint32_t descriptor_set,
602                           uint32_t binding) {
603   samplers_.push_back(SamplerInfo{sampler});
604 
605   auto& info = samplers_.back();
606   info.descriptor_set = descriptor_set;
607   info.binding = binding;
608   info.mask = std::numeric_limits<uint32_t>::max();
609 }
610 
AddSampler(Sampler * sampler,const std::string & arg_name)611 void Pipeline::AddSampler(Sampler* sampler, const std::string& arg_name) {
612   for (auto& info : samplers_) {
613     if (info.arg_name == arg_name) {
614       info.sampler = sampler;
615       return;
616     }
617   }
618 
619   samplers_.push_back(SamplerInfo{sampler});
620 
621   auto& info = samplers_.back();
622   info.arg_name = arg_name;
623   info.descriptor_set = std::numeric_limits<uint32_t>::max();
624   info.binding = std::numeric_limits<uint32_t>::max();
625   info.arg_no = std::numeric_limits<uint32_t>::max();
626   info.mask = std::numeric_limits<uint32_t>::max();
627 }
628 
AddSampler(Sampler * sampler,uint32_t arg_no)629 void Pipeline::AddSampler(Sampler* sampler, uint32_t arg_no) {
630   for (auto& info : samplers_) {
631     if (info.arg_no == arg_no) {
632       info.sampler = sampler;
633       return;
634     }
635   }
636 
637   samplers_.push_back(SamplerInfo{sampler});
638 
639   auto& info = samplers_.back();
640   info.arg_no = arg_no;
641   info.descriptor_set = std::numeric_limits<uint32_t>::max();
642   info.binding = std::numeric_limits<uint32_t>::max();
643   info.mask = std::numeric_limits<uint32_t>::max();
644 }
645 
AddSampler(uint32_t mask,uint32_t descriptor_set,uint32_t binding)646 void Pipeline::AddSampler(uint32_t mask,
647                           uint32_t descriptor_set,
648                           uint32_t binding) {
649   samplers_.push_back(SamplerInfo{nullptr});
650 
651   auto& info = samplers_.back();
652   info.arg_name = "";
653   info.arg_no = std::numeric_limits<uint32_t>::max();
654   info.mask = mask;
655   info.descriptor_set = descriptor_set;
656   info.binding = binding;
657 }
658 
ClearSamplers(uint32_t descriptor_set,uint32_t binding)659 void Pipeline::ClearSamplers(uint32_t descriptor_set, uint32_t binding) {
660   samplers_.erase(
661       std::remove_if(samplers_.begin(), samplers_.end(),
662                      [descriptor_set, binding](SamplerInfo& info) -> bool {
663                        return (info.descriptor_set == descriptor_set &&
664                                info.binding == binding);
665                      }),
666       samplers_.end());
667 }
668 
UpdateOpenCLBufferBindings()669 Result Pipeline::UpdateOpenCLBufferBindings() {
670   if (!IsCompute() || GetShaders().empty() ||
671       GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
672     return {};
673   }
674 
675   const auto& shader_info = GetShaders()[0];
676   const auto& descriptor_map = shader_info.GetDescriptorMap();
677   if (descriptor_map.empty())
678     return {};
679 
680   const auto iter = descriptor_map.find(shader_info.GetEntryPoint());
681   if (iter == descriptor_map.end())
682     return {};
683 
684   for (auto& info : samplers_) {
685     if (info.descriptor_set == std::numeric_limits<uint32_t>::max() &&
686         info.binding == std::numeric_limits<uint32_t>::max()) {
687       for (const auto& entry : iter->second) {
688         if (entry.arg_name == info.arg_name ||
689             entry.arg_ordinal == info.arg_no) {
690           if (entry.kind !=
691               Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SAMPLER) {
692             return Result("Sampler bound to non-sampler kernel arg");
693           }
694           info.descriptor_set = entry.descriptor_set;
695           info.binding = entry.binding;
696         }
697       }
698     }
699   }
700 
701   for (auto& info : buffers_) {
702     if (info.descriptor_set == std::numeric_limits<uint32_t>::max() &&
703         info.binding == std::numeric_limits<uint32_t>::max()) {
704       for (const auto& entry : iter->second) {
705         if (entry.arg_name == info.arg_name ||
706             entry.arg_ordinal == info.arg_no) {
707           // Buffer storage class consistency checks.
708           if (info.type == BufferType::kUnknown) {
709             // Set the appropriate buffer type.
710             switch (entry.kind) {
711               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO:
712               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO:
713                 info.type = BufferType::kUniform;
714                 break;
715               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO:
716               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD:
717                 info.type = BufferType::kStorage;
718                 break;
719               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE:
720                 info.type = BufferType::kSampledImage;
721                 break;
722               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE:
723                 info.type = BufferType::kStorageImage;
724                 break;
725               default:
726                 return Result("Unhandled buffer type for OPENCL-C shader");
727             }
728           } else if (info.type == BufferType::kUniform) {
729             if (entry.kind !=
730                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO &&
731                 entry.kind !=
732                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO) {
733               return Result("Buffer " + info.buffer->GetName() +
734                             " must be a uniform binding");
735             }
736           } else if (info.type == BufferType::kStorage) {
737             if (entry.kind !=
738                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO &&
739                 entry.kind !=
740                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD) {
741               return Result("Buffer " + info.buffer->GetName() +
742                             " must be a storage binding");
743             }
744           } else if (info.type == BufferType::kSampledImage) {
745             if (entry.kind !=
746                 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE) {
747               return Result("Buffer " + info.buffer->GetName() +
748                             " must be a read-only image binding");
749             }
750           } else if (info.type == BufferType::kStorageImage) {
751             if (entry.kind !=
752                 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE) {
753               return Result("Buffer " + info.buffer->GetName() +
754                             " must be a write-only image binding");
755             }
756           } else {
757             return Result("Unhandled buffer type for OPENCL-C shader");
758           }
759           info.descriptor_set = entry.descriptor_set;
760           info.binding = entry.binding;
761         }
762       }
763     }
764   }
765 
766   return {};
767 }
768 
GenerateOpenCLPodBuffers()769 Result Pipeline::GenerateOpenCLPodBuffers() {
770   if (!IsCompute() || GetShaders().empty() ||
771       GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
772     return {};
773   }
774 
775   const auto& shader_info = GetShaders()[0];
776   const auto& descriptor_map = shader_info.GetDescriptorMap();
777   if (descriptor_map.empty())
778     return {};
779 
780   const auto iter = descriptor_map.find(shader_info.GetEntryPoint());
781   if (iter == descriptor_map.end())
782     return {};
783 
784   // For each SET command, do the following:
785   // 1. Find the descriptor map entry for that argument.
786   // 2. Find or create the buffer for the descriptor set and binding pair.
787   // 3. Write the data for the SET command at the right offset.
788   for (const auto& arg_info : SetArgValues()) {
789     uint32_t descriptor_set = std::numeric_limits<uint32_t>::max();
790     uint32_t binding = std::numeric_limits<uint32_t>::max();
791     uint32_t offset = 0;
792     uint32_t arg_size = 0;
793     bool uses_name = !arg_info.name.empty();
794     Pipeline::ShaderInfo::DescriptorMapEntry::Kind kind =
795         Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
796     for (const auto& entry : iter->second) {
797       if (entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD &&
798           entry.kind !=
799               Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO &&
800           entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::
801                             POD_PUSHCONSTANT) {
802         continue;
803       }
804 
805       // Found the right entry.
806       if ((uses_name && entry.arg_name == arg_info.name) ||
807           entry.arg_ordinal == arg_info.ordinal) {
808         descriptor_set = entry.descriptor_set;
809         binding = entry.binding;
810         offset = entry.pod_offset;
811         arg_size = entry.pod_arg_size;
812         kind = entry.kind;
813         break;
814       }
815     }
816 
817     Buffer* buffer = nullptr;
818     if (kind ==
819         Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT) {
820       if (GetPushConstantBuffer().buffer == nullptr) {
821         auto r = CreatePushConstantBuffer();
822         if (!r.IsSuccess())
823           return r;
824       }
825       buffer = GetPushConstantBuffer().buffer;
826     } else {
827       if (descriptor_set == std::numeric_limits<uint32_t>::max() ||
828           binding == std::numeric_limits<uint32_t>::max()) {
829         std::string message =
830             "could not find descriptor map entry for SET command: kernel " +
831             shader_info.GetEntryPoint();
832         if (uses_name) {
833           message += ", name " + arg_info.name;
834         } else {
835           message += ", number " + std::to_string(arg_info.ordinal);
836         }
837         return Result(message);
838       }
839 
840       auto buf_iter = opencl_pod_buffer_map_.lower_bound(
841           std::make_pair(descriptor_set, binding));
842       if (buf_iter == opencl_pod_buffer_map_.end() ||
843           buf_iter->first.first != descriptor_set ||
844           buf_iter->first.second != binding) {
845         // Ensure no buffer was previously bound for this descriptor set and
846         // binding pair.
847         for (const auto& buf_info : GetBuffers()) {
848           if (buf_info.descriptor_set == descriptor_set &&
849               buf_info.binding == binding) {
850             return Result("previously bound buffer " +
851                           buf_info.buffer->GetName() +
852                           " to PoD args at descriptor set " +
853                           std::to_string(descriptor_set) + " binding " +
854                           std::to_string(binding));
855           }
856         }
857 
858         // Add a new buffer for this descriptor set and binding.
859         opencl_pod_buffers_.push_back(MakeUnique<Buffer>());
860         buffer = opencl_pod_buffers_.back().get();
861         auto buffer_type =
862             kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD
863                 ? BufferType::kStorage
864                 : BufferType::kUniform;
865 
866         // Use an 8-bit type because all the data in the descriptor map is
867         // byte-based and it simplifies the logic for sizing below.
868         TypeParser parser;
869         auto type = parser.Parse("R8_UINT");
870         auto fmt = MakeUnique<Format>(type.get());
871         buffer->SetFormat(fmt.get());
872         formats_.push_back(std::move(fmt));
873         types_.push_back(std::move(type));
874 
875         buffer->SetName(GetName() + "_pod_buffer_" +
876                         std::to_string(descriptor_set) + "_" +
877                         std::to_string(binding));
878         opencl_pod_buffer_map_.insert(
879             buf_iter,
880             std::make_pair(std::make_pair(descriptor_set, binding), buffer));
881         AddBuffer(buffer, buffer_type, descriptor_set, binding, 0, 0, 0, ~0ULL);
882       } else {
883         buffer = buf_iter->second;
884       }
885 
886       // Resize if necessary.
887       if (buffer->ValueCount() < offset + arg_size) {
888         buffer->SetSizeInElements(offset + arg_size);
889       }
890 
891       // Check the data size.
892       if (arg_size != arg_info.fmt->SizeInBytes()) {
893         std::string message = "SET command uses incorrect data size: kernel " +
894                               shader_info.GetEntryPoint();
895         if (uses_name) {
896           message += ", name " + arg_info.name;
897         } else {
898           message += ", number " + std::to_string(arg_info.ordinal);
899         }
900         return Result(message);
901       }
902     }
903 
904     // Convert the argument value into bytes. Currently, only scalar arguments
905     // are supported.
906     const auto arg_byte_size = arg_info.fmt->SizeInBytes();
907     std::vector<Value> data_bytes;
908     for (uint32_t i = 0; i < arg_byte_size; ++i) {
909       Value v;
910       if (arg_info.value.IsFloat()) {
911         if (arg_byte_size == sizeof(double)) {
912           union {
913             uint64_t u;
914             double d;
915           } u;
916           u.d = arg_info.value.AsDouble();
917           v.SetIntValue((u.u >> (i * 8)) & 0xff);
918         } else {
919           union {
920             uint32_t u;
921             float f;
922           } u;
923           u.f = arg_info.value.AsFloat();
924           v.SetIntValue((u.u >> (i * 8)) & 0xff);
925         }
926       } else {
927         v.SetIntValue((arg_info.value.AsUint64() >> (i * 8)) & 0xff);
928       }
929       data_bytes.push_back(v);
930     }
931     Result r = buffer->SetDataWithOffset(data_bytes, offset);
932     if (!r.IsSuccess())
933       return r;
934   }
935 
936   return {};
937 }
938 
GenerateOpenCLLiteralSamplers()939 Result Pipeline::GenerateOpenCLLiteralSamplers() {
940   for (auto& info : samplers_) {
941     if (info.sampler || info.mask == std::numeric_limits<uint32_t>::max())
942       continue;
943 
944     auto literal_sampler = MakeUnique<Sampler>();
945     literal_sampler->SetName("literal." + std::to_string(info.descriptor_set) +
946                              "." + std::to_string(info.binding));
947 
948     // The values for addressing modes, filtering modes and coordinate
949     // normalization are all defined in the OpenCL header.
950 
951     literal_sampler->SetNormalizedCoords(info.mask &
952                                          kOpenCLNormalizedCoordsBit);
953 
954     uint32_t addressing_bits = info.mask & kOpenCLAddressModeBits;
955     AddressMode addressing_mode = AddressMode::kUnknown;
956     if (addressing_bits == kOpenCLAddressModeNone ||
957         addressing_bits == kOpenCLAddressModeClampToEdge) {
958       // CLK_ADDRESS_NONE
959       // CLK_ADDERSS_CLAMP_TO_EDGE
960       addressing_mode = AddressMode::kClampToEdge;
961     } else if (addressing_bits == kOpenCLAddressModeClamp) {
962       // CLK_ADDRESS_CLAMP
963       addressing_mode = AddressMode::kClampToBorder;
964     } else if (addressing_bits == kOpenCLAddressModeRepeat) {
965       // CLK_ADDRESS_REPEAT
966       addressing_mode = AddressMode::kRepeat;
967     } else if (addressing_bits == kOpenCLAddressModeMirroredRepeat) {
968       // CLK_ADDRESS_MIRRORED_REPEAT
969       addressing_mode = AddressMode::kMirroredRepeat;
970     }
971     literal_sampler->SetAddressModeU(addressing_mode);
972     literal_sampler->SetAddressModeV(addressing_mode);
973     // TODO(alan-baker): If this is used with an arrayed image then W should use
974     // kClampToEdge always, but this information is not currently available.
975     literal_sampler->SetAddressModeW(addressing_mode);
976 
977     // Next bit is filtering mode.
978     FilterType filtering_mode = FilterType::kUnknown;
979     if (info.mask & kOpenCLFilterModeNearestBit) {
980       filtering_mode = FilterType::kNearest;
981     } else if (info.mask & kOpenCLFilterModeLinearBit) {
982       filtering_mode = FilterType::kLinear;
983     }
984     literal_sampler->SetMagFilter(filtering_mode);
985     literal_sampler->SetMinFilter(filtering_mode);
986 
987     // TODO(alan-baker): OpenCL wants the border color to be based on image
988     // channel orders which aren't accessible.
989 
990     // clspv never generates multiple MIPMAP levels.
991     literal_sampler->SetMinLOD(0.0f);
992     literal_sampler->SetMaxLOD(0.0f);
993 
994     opencl_literal_samplers_.push_back(std::move(literal_sampler));
995     info.sampler = opencl_literal_samplers_.back().get();
996   }
997 
998   return {};
999 }
1000 
GenerateOpenCLPushConstants()1001 Result Pipeline::GenerateOpenCLPushConstants() {
1002   if (!IsCompute() || GetShaders().empty() ||
1003       GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
1004     return {};
1005   }
1006 
1007   const auto& shader_info = GetShaders()[0];
1008   if (shader_info.GetPushConstants().empty())
1009     return {};
1010 
1011   Result r = CreatePushConstantBuffer();
1012   if (!r.IsSuccess())
1013     return r;
1014 
1015   auto* buf = GetPushConstantBuffer().buffer;
1016   assert(buf);
1017 
1018   // Determine size and contents of the push constant buffer.
1019   for (const auto& pc : shader_info.GetPushConstants()) {
1020     assert(pc.size % sizeof(uint32_t) == 0);
1021     assert(pc.offset % sizeof(uint32_t) == 0);
1022 
1023     if (buf->GetSizeInBytes() < pc.offset + pc.size)
1024       buf->SetSizeInBytes(pc.offset + pc.size);
1025 
1026     std::vector<uint32_t> bytes(pc.size / sizeof(uint32_t));
1027     uint32_t base = 0;
1028     switch (pc.type) {
1029       case Pipeline::ShaderInfo::PushConstant::PushConstantType::kDimensions:
1030         // All compute kernel launches are 3D.
1031         bytes[base] = 3;
1032         break;
1033       case Pipeline::ShaderInfo::PushConstant::PushConstantType::kGlobalOffset:
1034         // Global offsets are not currently supported.
1035         bytes[base] = 0;
1036         bytes[base + 1] = 0;
1037         bytes[base + 2] = 0;
1038         break;
1039       case Pipeline::ShaderInfo::PushConstant::PushConstantType::kRegionOffset:
1040         // Region offsets are not currently supported.
1041         bytes[base] = 0;
1042         bytes[base + 1] = 0;
1043         bytes[base + 2] = 0;
1044         break;
1045     }
1046     memcpy(buf->ValuePtr()->data() + pc.offset, bytes.data(),
1047            bytes.size() * sizeof(uint32_t));
1048   }
1049 
1050   return {};
1051 }
1052 
1053 }  // namespace amber
1054