• 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 <limits>
19 #include <set>
20 
21 #include "src/make_unique.h"
22 #include "src/type_parser.h"
23 
24 namespace amber {
25 namespace {
26 
27 const char* kDefaultColorBufferFormat = "B8G8R8A8_UNORM";
28 const char* kDefaultDepthBufferFormat = "D32_SFLOAT_S8_UINT";
29 
30 }  // namespace
31 
32 const char* Pipeline::kGeneratedColorBuffer = "framebuffer";
33 const char* Pipeline::kGeneratedDepthBuffer = "depth_buffer";
34 
ShaderInfo(Shader * shader,ShaderType type)35 Pipeline::ShaderInfo::ShaderInfo(Shader* shader, ShaderType type)
36     : shader_(shader), shader_type_(type), entry_point_("main") {}
37 
38 Pipeline::ShaderInfo::ShaderInfo(const ShaderInfo&) = default;
39 
40 Pipeline::ShaderInfo::~ShaderInfo() = default;
41 
Pipeline(PipelineType type)42 Pipeline::Pipeline(PipelineType type) : pipeline_type_(type) {}
43 
44 Pipeline::~Pipeline() = default;
45 
Clone() const46 std::unique_ptr<Pipeline> Pipeline::Clone() const {
47   auto clone = MakeUnique<Pipeline>(pipeline_type_);
48   clone->shaders_ = shaders_;
49   clone->color_attachments_ = color_attachments_;
50   clone->vertex_buffers_ = vertex_buffers_;
51   clone->buffers_ = buffers_;
52   clone->depth_buffer_ = depth_buffer_;
53   clone->index_buffer_ = index_buffer_;
54   clone->fb_width_ = fb_width_;
55   clone->fb_height_ = fb_height_;
56   clone->set_arg_values_ = set_arg_values_;
57 
58   if (!opencl_pod_buffers_.empty()) {
59     // Generate specific buffers for the clone.
60     clone->GenerateOpenCLPodBuffers();
61   }
62 
63   return clone;
64 }
65 
AddShader(Shader * shader,ShaderType shader_type)66 Result Pipeline::AddShader(Shader* shader, ShaderType shader_type) {
67   if (!shader)
68     return Result("shader can not be null when attached to pipeline");
69 
70   if (pipeline_type_ == PipelineType::kCompute &&
71       shader_type != kShaderTypeCompute) {
72     return Result("only compute shaders allowed in a compute pipeline");
73   }
74   if (pipeline_type_ == PipelineType::kGraphics &&
75       shader_type == kShaderTypeCompute) {
76     return Result("can not add a compute shader to a graphics pipeline");
77   }
78 
79   for (auto& info : shaders_) {
80     const auto* is = info.GetShader();
81     if (is == shader)
82       return Result("can not add duplicate shader to pipeline");
83     if (is->GetType() == shader_type) {
84       info.SetShader(shader);
85       return {};
86     }
87   }
88 
89   shaders_.emplace_back(shader, shader_type);
90   return {};
91 }
92 
SetShaderOptimizations(const Shader * shader,const std::vector<std::string> & opts)93 Result Pipeline::SetShaderOptimizations(const Shader* shader,
94                                         const std::vector<std::string>& opts) {
95   if (!shader)
96     return Result("invalid shader specified for optimizations");
97 
98   std::set<std::string> seen;
99   for (const auto& opt : opts) {
100     if (seen.count(opt) != 0)
101       return Result("duplicate optimization flag (" + opt + ") set on shader");
102 
103     seen.insert(opt);
104   }
105 
106   for (auto& info : shaders_) {
107     const auto* is = info.GetShader();
108     if (is == shader) {
109       info.SetShaderOptimizations(opts);
110       return {};
111     }
112   }
113 
114   return Result("unknown shader specified for optimizations: " +
115                 shader->GetName());
116 }
117 
SetShaderCompileOptions(const Shader * shader,const std::vector<std::string> & opts)118 Result Pipeline::SetShaderCompileOptions(const Shader* shader,
119                                          const std::vector<std::string>& opts) {
120   if (!shader)
121     return Result("invalid shader specified for compile options");
122 
123   for (auto& info : shaders_) {
124     const auto* is = info.GetShader();
125     if (is == shader) {
126       info.SetCompileOptions(opts);
127       return {};
128     }
129   }
130 
131   return Result("unknown shader specified for compile options: " +
132                 shader->GetName());
133 }
134 
SetShaderEntryPoint(const Shader * shader,const std::string & name)135 Result Pipeline::SetShaderEntryPoint(const Shader* shader,
136                                      const std::string& name) {
137   if (!shader)
138     return Result("invalid shader specified for entry point");
139   if (name.empty())
140     return Result("entry point should not be blank");
141 
142   for (auto& info : shaders_) {
143     if (info.GetShader() == shader) {
144       if (info.GetEntryPoint() != "main")
145         return Result("multiple entry points given for the same shader");
146 
147       info.SetEntryPoint(name);
148       return {};
149     }
150   }
151 
152   return Result("unknown shader specified for entry point: " +
153                 shader->GetName());
154 }
155 
SetShaderType(const Shader * shader,ShaderType type)156 Result Pipeline::SetShaderType(const Shader* shader, ShaderType type) {
157   if (!shader)
158     return Result("invalid shader specified for shader type");
159 
160   for (auto& info : shaders_) {
161     if (info.GetShader() == shader) {
162       info.SetShaderType(type);
163       return {};
164     }
165   }
166 
167   return Result("unknown shader specified for shader type: " +
168                 shader->GetName());
169 }
170 
Validate() const171 Result Pipeline::Validate() const {
172   size_t fb_size = fb_width_ * fb_height_;
173   for (const auto& attachment : color_attachments_) {
174     if (attachment.buffer->ElementCount() != fb_size) {
175       return Result(
176           "shared framebuffer must have same size over all PIPELINES");
177     }
178   }
179 
180   if (depth_buffer_.buffer && depth_buffer_.buffer->ElementCount() != fb_size)
181     return Result("shared depth buffer must have same size over all PIPELINES");
182 
183   for (auto& buf : GetBuffers()) {
184     if (buf.buffer->GetFormat() == nullptr) {
185       return Result("buffer (" + std::to_string(buf.descriptor_set) + ":" +
186                     std::to_string(buf.binding) + ") requires a format");
187     }
188   }
189 
190   if (pipeline_type_ == PipelineType::kGraphics)
191     return ValidateGraphics();
192   return ValidateCompute();
193 }
194 
ValidateGraphics() const195 Result Pipeline::ValidateGraphics() const {
196   if (color_attachments_.empty())
197     return Result("PIPELINE missing color attachment");
198 
199   bool found_vertex = false;
200   for (const auto& info : shaders_) {
201     const auto* s = info.GetShader();
202     if (s->GetType() == kShaderTypeVertex) {
203       found_vertex = true;
204       break;
205     }
206   }
207 
208   if (!found_vertex)
209     return Result("graphics pipeline requires a vertex shader");
210   return {};
211 }
212 
ValidateCompute() const213 Result Pipeline::ValidateCompute() const {
214   if (shaders_.empty())
215     return Result("compute pipeline requires a compute shader");
216 
217   return {};
218 }
219 
UpdateFramebufferSizes()220 void Pipeline::UpdateFramebufferSizes() {
221   uint32_t size = fb_width_ * fb_height_;
222   if (size == 0)
223     return;
224 
225   for (auto& attachment : color_attachments_) {
226     attachment.buffer->SetWidth(fb_width_);
227     attachment.buffer->SetHeight(fb_height_);
228     attachment.buffer->SetElementCount(size);
229   }
230 
231   if (depth_buffer_.buffer) {
232     depth_buffer_.buffer->SetWidth(fb_width_);
233     depth_buffer_.buffer->SetHeight(fb_height_);
234     depth_buffer_.buffer->SetElementCount(size);
235   }
236 }
237 
AddColorAttachment(Buffer * buf,uint32_t location)238 Result Pipeline::AddColorAttachment(Buffer* buf, uint32_t location) {
239   for (const auto& attachment : color_attachments_) {
240     if (attachment.location == location)
241       return Result("can not bind two color buffers to the same LOCATION");
242     if (attachment.buffer == buf)
243       return Result("color buffer may only be bound to a PIPELINE once");
244   }
245 
246   color_attachments_.push_back(BufferInfo{buf});
247 
248   auto& info = color_attachments_.back();
249   info.location = location;
250   buf->SetWidth(fb_width_);
251   buf->SetHeight(fb_height_);
252   buf->SetElementCount(fb_width_ * fb_height_);
253   return {};
254 }
255 
GetLocationForColorAttachment(Buffer * buf,uint32_t * loc) const256 Result Pipeline::GetLocationForColorAttachment(Buffer* buf,
257                                                uint32_t* loc) const {
258   for (const auto& info : color_attachments_) {
259     if (info.buffer == buf) {
260       *loc = info.location;
261       return {};
262     }
263   }
264   return Result("Unable to find requested buffer");
265 }
266 
SetDepthBuffer(Buffer * buf)267 Result Pipeline::SetDepthBuffer(Buffer* buf) {
268   if (depth_buffer_.buffer != nullptr)
269     return Result("can only bind one depth buffer in a PIPELINE");
270   if (buf->GetBufferType() != BufferType::kDepth)
271     return Result("expected a depth buffer");
272 
273   depth_buffer_.buffer = buf;
274   buf->SetWidth(fb_width_);
275   buf->SetHeight(fb_height_);
276   buf->SetElementCount(fb_width_ * fb_height_);
277   return {};
278 }
279 
SetIndexBuffer(Buffer * buf)280 Result Pipeline::SetIndexBuffer(Buffer* buf) {
281   if (index_buffer_ != nullptr)
282     return Result("can only bind one INDEX_DATA buffer in a pipeline");
283 
284   index_buffer_ = buf;
285   return {};
286 }
287 
AddVertexBuffer(Buffer * buf,uint32_t location)288 Result Pipeline::AddVertexBuffer(Buffer* buf, uint32_t location) {
289   for (const auto& vtex : vertex_buffers_) {
290     if (vtex.location == location)
291       return Result("can not bind two vertex buffers to the same LOCATION");
292     if (vtex.buffer == buf)
293       return Result("vertex buffer may only be bound to a PIPELINE once");
294   }
295   if (buf->GetBufferType() != BufferType::kVertex)
296     return Result("expected a vertex buffer");
297 
298   vertex_buffers_.push_back(BufferInfo{buf});
299   vertex_buffers_.back().location = location;
300   return {};
301 }
302 
SetPushConstantBuffer(Buffer * buf)303 Result Pipeline::SetPushConstantBuffer(Buffer* buf) {
304   if (push_constant_buffer_.buffer != nullptr)
305     return Result("can only bind one push constant buffer in a PIPELINE");
306   if (buf->GetBufferType() != BufferType::kPushConstant)
307     return Result("expected a push constant buffer");
308 
309   push_constant_buffer_.buffer = buf;
310   return {};
311 }
312 
GenerateDefaultColorAttachmentBuffer()313 std::unique_ptr<Buffer> Pipeline::GenerateDefaultColorAttachmentBuffer() {
314   TypeParser parser;
315   auto type = parser.Parse(kDefaultColorBufferFormat);
316   auto fmt = MakeUnique<Format>(type.get());
317 
318   std::unique_ptr<Buffer> buf = MakeUnique<Buffer>(BufferType::kColor);
319   buf->SetName(kGeneratedColorBuffer);
320   buf->SetFormat(fmt.get());
321 
322   formats_.push_back(std::move(fmt));
323   types_.push_back(std::move(type));
324   return buf;
325 }
326 
GenerateDefaultDepthAttachmentBuffer()327 std::unique_ptr<Buffer> Pipeline::GenerateDefaultDepthAttachmentBuffer() {
328   TypeParser parser;
329   auto type = parser.Parse(kDefaultDepthBufferFormat);
330   auto fmt = MakeUnique<Format>(type.get());
331 
332   std::unique_ptr<Buffer> buf = MakeUnique<Buffer>(BufferType::kDepth);
333   buf->SetName(kGeneratedDepthBuffer);
334   buf->SetFormat(fmt.get());
335 
336   formats_.push_back(std::move(fmt));
337   types_.push_back(std::move(type));
338   return buf;
339 }
340 
GetBufferForBinding(uint32_t descriptor_set,uint32_t binding) const341 Buffer* Pipeline::GetBufferForBinding(uint32_t descriptor_set,
342                                       uint32_t binding) const {
343   for (const auto& info : buffers_) {
344     if (info.descriptor_set == descriptor_set && info.binding == binding)
345       return info.buffer;
346   }
347   return nullptr;
348 }
349 
AddBuffer(Buffer * buf,uint32_t descriptor_set,uint32_t binding)350 void Pipeline::AddBuffer(Buffer* buf,
351                          uint32_t descriptor_set,
352                          uint32_t binding) {
353   // If this buffer binding already exists, overwrite with the new buffer.
354   for (auto& info : buffers_) {
355     if (info.descriptor_set == descriptor_set && info.binding == binding) {
356       info.buffer = buf;
357       return;
358     }
359   }
360 
361   buffers_.push_back(BufferInfo{buf});
362 
363   auto& info = buffers_.back();
364   info.descriptor_set = descriptor_set;
365   info.binding = binding;
366 }
367 
AddBuffer(Buffer * buf,const std::string & arg_name)368 void Pipeline::AddBuffer(Buffer* buf, const std::string& arg_name) {
369   // If this buffer binding already exists, overwrite with the new buffer.
370   for (auto& info : buffers_) {
371     if (info.arg_name == arg_name) {
372       info.buffer = buf;
373       return;
374     }
375   }
376 
377   buffers_.push_back(BufferInfo{buf});
378 
379   auto& info = buffers_.back();
380   info.arg_name = arg_name;
381   info.descriptor_set = std::numeric_limits<uint32_t>::max();
382   info.binding = std::numeric_limits<uint32_t>::max();
383   info.arg_no = std::numeric_limits<uint32_t>::max();
384 }
385 
AddBuffer(Buffer * buf,uint32_t arg_no)386 void Pipeline::AddBuffer(Buffer* buf, uint32_t arg_no) {
387   // If this buffer binding already exists, overwrite with the new buffer.
388   for (auto& info : buffers_) {
389     if (info.arg_no == arg_no) {
390       info.buffer = buf;
391       return;
392     }
393   }
394 
395   buffers_.push_back(BufferInfo{buf});
396 
397   auto& info = buffers_.back();
398   info.arg_no = arg_no;
399   info.descriptor_set = std::numeric_limits<uint32_t>::max();
400   info.binding = std::numeric_limits<uint32_t>::max();
401 }
402 
UpdateOpenCLBufferBindings()403 Result Pipeline::UpdateOpenCLBufferBindings() {
404   if (!IsCompute() || GetShaders().empty() ||
405       GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC)
406     return {};
407 
408   const auto& shader_info = GetShaders()[0];
409   const auto& descriptor_map = shader_info.GetDescriptorMap();
410   if (descriptor_map.empty())
411     return {};
412 
413   const auto iter = descriptor_map.find(shader_info.GetEntryPoint());
414   if (iter == descriptor_map.end())
415     return {};
416 
417   for (auto& info : buffers_) {
418     if (info.descriptor_set == std::numeric_limits<uint32_t>::max() &&
419         info.binding == std::numeric_limits<uint32_t>::max()) {
420       for (const auto& entry : iter->second) {
421         if (entry.arg_name == info.arg_name ||
422             entry.arg_ordinal == info.arg_no) {
423           // Buffer storage class consistency checks.
424           if (info.buffer->GetBufferType() == BufferType::kUnknown) {
425             // Set the appropriate buffer type.
426             switch (entry.kind) {
427               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO:
428               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO:
429                 info.buffer->SetBufferType(BufferType::kUniform);
430                 break;
431               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO:
432               case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD:
433                 info.buffer->SetBufferType(BufferType::kStorage);
434                 break;
435               default:
436                 return Result("Unhandled buffer type for OPENCL-C shader");
437             }
438           } else if (info.buffer->GetBufferType() == BufferType::kUniform) {
439             if (entry.kind !=
440                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO &&
441                 entry.kind !=
442                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO) {
443               return Result("Buffer " + info.buffer->GetName() +
444                             " must be an uniform binding");
445             }
446           } else if (info.buffer->GetBufferType() == BufferType::kStorage) {
447             if (entry.kind !=
448                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO &&
449                 entry.kind !=
450                     Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD) {
451               return Result("Buffer " + info.buffer->GetName() +
452                             " must be a storage binding");
453             }
454           } else {
455             return Result("Unhandled buffer type for OPENCL-C shader");
456           }
457           info.descriptor_set = entry.descriptor_set;
458           info.binding = entry.binding;
459         }
460       }
461     }
462   }
463 
464   return {};
465 }
466 
GenerateOpenCLPodBuffers()467 Result Pipeline::GenerateOpenCLPodBuffers() {
468   if (!IsCompute() || GetShaders().empty() ||
469       GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
470     return {};
471   }
472 
473   const auto& shader_info = GetShaders()[0];
474   const auto& descriptor_map = shader_info.GetDescriptorMap();
475   if (descriptor_map.empty())
476     return {};
477 
478   const auto iter = descriptor_map.find(shader_info.GetEntryPoint());
479   if (iter == descriptor_map.end())
480     return {};
481 
482   // For each SET command, do the following:
483   // 1. Find the descriptor map entry for that argument.
484   // 2. Find or create the buffer for the descriptor set and binding pair.
485   // 3. Write the data for the SET command at the right offset.
486   for (const auto& arg_info : SetArgValues()) {
487     uint32_t descriptor_set = std::numeric_limits<uint32_t>::max();
488     uint32_t binding = std::numeric_limits<uint32_t>::max();
489     uint32_t offset = 0;
490     uint32_t arg_size = 0;
491     bool uses_name = !arg_info.name.empty();
492     Pipeline::ShaderInfo::DescriptorMapEntry::Kind kind =
493         Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
494     for (const auto& entry : iter->second) {
495       if (entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD &&
496           entry.kind !=
497               Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO) {
498         continue;
499       }
500 
501       // Found the right entry.
502       if ((uses_name && entry.arg_name == arg_info.name) ||
503           entry.arg_ordinal == arg_info.ordinal) {
504         descriptor_set = entry.descriptor_set;
505         binding = entry.binding;
506         offset = entry.pod_offset;
507         arg_size = entry.pod_arg_size;
508         kind = entry.kind;
509         break;
510       }
511     }
512 
513     if (descriptor_set == std::numeric_limits<uint32_t>::max() ||
514         binding == std::numeric_limits<uint32_t>::max()) {
515       std::string message =
516           "could not find descriptor map entry for SET command: kernel " +
517           shader_info.GetEntryPoint();
518       if (uses_name) {
519         message += ", name " + arg_info.name;
520       } else {
521         message += ", number " + std::to_string(arg_info.ordinal);
522       }
523       return Result(message);
524     }
525 
526     auto buf_iter = opencl_pod_buffer_map_.lower_bound(
527         std::make_pair(descriptor_set, binding));
528     Buffer* buffer = nullptr;
529     if (buf_iter == opencl_pod_buffer_map_.end() ||
530         buf_iter->first.first != descriptor_set ||
531         buf_iter->first.second != binding) {
532       // Ensure no buffer was previously bound for this descriptor set and
533       // binding pair.
534       for (const auto& buf_info : GetBuffers()) {
535         if (buf_info.descriptor_set == descriptor_set &&
536             buf_info.binding == binding) {
537           return Result("previously bound buffer " +
538                         buf_info.buffer->GetName() +
539                         " to PoD args at descriptor set " +
540                         std::to_string(descriptor_set) + " binding " +
541                         std::to_string(binding));
542         }
543       }
544 
545       // Add a new buffer for this descriptor set and binding.
546       opencl_pod_buffers_.push_back(MakeUnique<Buffer>());
547       buffer = opencl_pod_buffers_.back().get();
548       buffer->SetBufferType(
549           kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD
550               ? BufferType::kStorage
551               : BufferType::kUniform);
552 
553       // Use an 8-bit type because all the data in the descriptor map is
554       // byte-based and it simplifies the logic for sizing below.
555       TypeParser parser;
556       auto type = parser.Parse("R8_UINT");
557       auto fmt = MakeUnique<Format>(type.get());
558       buffer->SetFormat(fmt.get());
559       formats_.push_back(std::move(fmt));
560       types_.push_back(std::move(type));
561 
562       buffer->SetName(GetName() + "_pod_buffer_" +
563                       std::to_string(descriptor_set) + "_" +
564                       std::to_string(binding));
565       opencl_pod_buffer_map_.insert(
566           buf_iter,
567           std::make_pair(std::make_pair(descriptor_set, binding), buffer));
568       AddBuffer(buffer, descriptor_set, binding);
569     } else {
570       buffer = buf_iter->second;
571     }
572 
573     // Resize if necessary.
574     if (buffer->ValueCount() < offset + arg_size) {
575       buffer->SetSizeInElements(offset + arg_size);
576     }
577 
578     // Check the data size.
579     if (arg_size != arg_info.fmt->SizeInBytes()) {
580       std::string message = "SET command uses incorrect data size: kernel " +
581                             shader_info.GetEntryPoint();
582       if (uses_name) {
583         message += ", name " + arg_info.name;
584       } else {
585         message += ", number " + std::to_string(arg_info.ordinal);
586       }
587       return Result(message);
588     }
589 
590     Result r = buffer->SetDataWithOffset({arg_info.value}, offset);
591     if (!r.IsSuccess())
592       return r;
593   }
594 
595   return {};
596 }
597 
598 }  // namespace amber
599