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