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