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