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
60 Pipeline::ShaderInfo::ShaderInfo(const ShaderInfo&) = default;
61
62 Pipeline::ShaderInfo::~ShaderInfo() = default;
63
Pipeline(PipelineType type)64 Pipeline::Pipeline(PipelineType type) : pipeline_type_(type) {}
65
66 Pipeline::~Pipeline() = default;
67
Clone() const68 std::unique_ptr<Pipeline> Pipeline::Clone() const {
69 auto clone = MakeUnique<Pipeline>(pipeline_type_);
70 clone->shaders_ = shaders_;
71 clone->color_attachments_ = color_attachments_;
72 clone->vertex_buffers_ = vertex_buffers_;
73 clone->buffers_ = buffers_;
74 clone->depth_stencil_buffer_ = depth_stencil_buffer_;
75 clone->index_buffer_ = index_buffer_;
76 clone->fb_width_ = fb_width_;
77 clone->fb_height_ = fb_height_;
78 clone->set_arg_values_ = set_arg_values_;
79 clone->pipeline_data_ = pipeline_data_;
80
81 if (!opencl_pod_buffers_.empty()) {
82 // Generate specific buffers for the clone.
83 clone->GenerateOpenCLPodBuffers();
84 }
85
86 return clone;
87 }
88
AddShader(Shader * shader,ShaderType shader_type)89 Result Pipeline::AddShader(Shader* shader, ShaderType shader_type) {
90 if (!shader)
91 return Result("shader can not be null when attached to pipeline");
92
93 if (pipeline_type_ == PipelineType::kCompute &&
94 shader_type != kShaderTypeCompute) {
95 return Result("only compute shaders allowed in a compute pipeline");
96 }
97 if (pipeline_type_ == PipelineType::kGraphics &&
98 shader_type == kShaderTypeCompute) {
99 return Result("can not add a compute shader to a graphics pipeline");
100 }
101
102 for (auto& info : shaders_) {
103 const auto* is = info.GetShader();
104 if (is == shader)
105 return Result("can not add duplicate shader to pipeline");
106 if (is->GetType() == shader_type) {
107 info.SetShader(shader);
108 return {};
109 }
110 }
111
112 shaders_.emplace_back(shader, shader_type);
113 return {};
114 }
115
SetShaderOptimizations(const Shader * shader,const std::vector<std::string> & opts)116 Result Pipeline::SetShaderOptimizations(const Shader* shader,
117 const std::vector<std::string>& opts) {
118 if (!shader)
119 return Result("invalid shader specified for optimizations");
120
121 std::set<std::string> seen;
122 for (const auto& opt : opts) {
123 if (seen.count(opt) != 0)
124 return Result("duplicate optimization flag (" + opt + ") set on shader");
125
126 seen.insert(opt);
127 }
128
129 for (auto& info : shaders_) {
130 const auto* is = info.GetShader();
131 if (is == shader) {
132 info.SetShaderOptimizations(opts);
133 return {};
134 }
135 }
136
137 return Result("unknown shader specified for optimizations: " +
138 shader->GetName());
139 }
140
SetShaderCompileOptions(const Shader * shader,const std::vector<std::string> & opts)141 Result Pipeline::SetShaderCompileOptions(const Shader* shader,
142 const std::vector<std::string>& opts) {
143 if (!shader)
144 return Result("invalid shader specified for compile options");
145
146 for (auto& info : shaders_) {
147 const auto* is = info.GetShader();
148 if (is == shader) {
149 info.SetCompileOptions(opts);
150 return {};
151 }
152 }
153
154 return Result("unknown shader specified for compile options: " +
155 shader->GetName());
156 }
157
SetShaderRequiredSubgroupSize(const Shader * shader,const ShaderInfo::RequiredSubgroupSizeSetting setting,const uint32_t size)158 Result Pipeline::SetShaderRequiredSubgroupSize(
159 const Shader* shader,
160 const ShaderInfo::RequiredSubgroupSizeSetting setting,
161 const uint32_t size) {
162 if (!shader)
163 return Result("invalid shader specified for required subgroup size");
164
165 for (auto& info : shaders_) {
166 const auto* is = info.GetShader();
167 if (is == shader) {
168 info.SetRequiredSubgroupSizeSetting(setting, size);
169 return {};
170 }
171 }
172
173 return Result("unknown shader specified for required subgroup size: " +
174 shader->GetName());
175 }
176
SetShaderRequiredSubgroupSize(const Shader * shader,const uint32_t subgroupSize)177 Result Pipeline::SetShaderRequiredSubgroupSize(const Shader* shader,
178 const uint32_t subgroupSize) {
179 const bool isPow2 =
180 subgroupSize > 0 && (subgroupSize & (subgroupSize - 1)) == 0;
181 if (subgroupSize == 0 || subgroupSize > 128 || !isPow2) {
182 return Result("invalid required subgroup size " +
183 std::to_string(subgroupSize) + " specified for shader name " +
184 shader->GetName());
185 }
186 const ShaderInfo::RequiredSubgroupSizeSetting setting =
187 ShaderInfo::RequiredSubgroupSizeSetting::kSetToSpecificSize;
188 return SetShaderRequiredSubgroupSize(shader, setting, subgroupSize);
189 }
190
SetShaderRequiredSubgroupSizeToMinimum(const Shader * shader)191 Result Pipeline::SetShaderRequiredSubgroupSizeToMinimum(const Shader* shader) {
192 const ShaderInfo::RequiredSubgroupSizeSetting subgroupSizeSetting =
193 ShaderInfo::RequiredSubgroupSizeSetting::kSetToMinimumSize;
194 return SetShaderRequiredSubgroupSize(shader, subgroupSizeSetting, 0);
195 }
196
SetShaderRequiredSubgroupSizeToMaximum(const Shader * shader)197 Result Pipeline::SetShaderRequiredSubgroupSizeToMaximum(const Shader* shader) {
198 const ShaderInfo::RequiredSubgroupSizeSetting subgroupSizeSetting =
199 ShaderInfo::RequiredSubgroupSizeSetting::kSetToMaximumSize;
200 return SetShaderRequiredSubgroupSize(shader, subgroupSizeSetting, 0);
201 }
202
SetShaderVaryingSubgroupSize(const Shader * shader,const bool isSet)203 Result Pipeline::SetShaderVaryingSubgroupSize(const Shader* shader,
204 const bool isSet) {
205 if (!shader)
206 return Result("invalid shader specified for varying subgroup size");
207
208 for (auto& info : shaders_) {
209 const auto* is = info.GetShader();
210 if (is == shader) {
211 info.SetVaryingSubgroupSize(isSet);
212 return {};
213 }
214 }
215
216 return Result("unknown shader specified for varying subgroup size: " +
217 shader->GetName());
218 }
219
SetShaderRequireFullSubgroups(const Shader * shader,const bool isSet)220 Result Pipeline::SetShaderRequireFullSubgroups(const Shader* shader,
221 const bool isSet) {
222 if (!shader)
223 return Result("invalid shader specified for optimizations");
224
225 for (auto& info : shaders_) {
226 const auto* is = info.GetShader();
227 if (is == shader) {
228 info.SetRequireFullSubgroups(isSet);
229 return {};
230 }
231 }
232
233 return Result("unknown shader specified for optimizations: " +
234 shader->GetName());
235 }
236
SetShaderEntryPoint(const Shader * shader,const std::string & name)237 Result Pipeline::SetShaderEntryPoint(const Shader* shader,
238 const std::string& name) {
239 if (!shader)
240 return Result("invalid shader specified for entry point");
241 if (name.empty())
242 return Result("entry point should not be blank");
243
244 for (auto& info : shaders_) {
245 if (info.GetShader() == shader) {
246 if (info.GetEntryPoint() != "main")
247 return Result("multiple entry points given for the same shader");
248
249 info.SetEntryPoint(name);
250 return {};
251 }
252 }
253
254 return Result("unknown shader specified for entry point: " +
255 shader->GetName());
256 }
257
SetShaderType(const Shader * shader,ShaderType type)258 Result Pipeline::SetShaderType(const Shader* shader, ShaderType type) {
259 if (!shader)
260 return Result("invalid shader specified for shader type");
261
262 for (auto& info : shaders_) {
263 if (info.GetShader() == shader) {
264 info.SetShaderType(type);
265 return {};
266 }
267 }
268
269 return Result("unknown shader specified for shader type: " +
270 shader->GetName());
271 }
272
Validate() const273 Result Pipeline::Validate() const {
274 for (const auto& attachment : color_attachments_) {
275 if (attachment.buffer->ElementCount() !=
276 (fb_width_ << attachment.base_mip_level) *
277 (fb_height_ << attachment.base_mip_level)) {
278 return Result(
279 "shared framebuffer must have same size over all PIPELINES");
280 }
281 }
282
283 if (depth_stencil_buffer_.buffer &&
284 depth_stencil_buffer_.buffer->ElementCount() != fb_width_ * fb_height_) {
285 return Result("shared depth buffer must have same size over all PIPELINES");
286 }
287
288 for (auto& buf : GetBuffers()) {
289 if (buf.buffer->GetFormat() == nullptr) {
290 return Result("buffer (" + std::to_string(buf.descriptor_set) + ":" +
291 std::to_string(buf.binding) + ") requires a format");
292 }
293 }
294
295 if (pipeline_type_ == PipelineType::kGraphics)
296 return ValidateGraphics();
297
298 return ValidateCompute();
299 }
300
ValidateGraphics() const301 Result Pipeline::ValidateGraphics() const {
302 if (color_attachments_.empty())
303 return Result("PIPELINE missing color attachment");
304
305 bool found_vertex = false;
306 for (const auto& info : shaders_) {
307 const auto* s = info.GetShader();
308 if (s->GetType() == kShaderTypeVertex) {
309 found_vertex = true;
310 break;
311 }
312 }
313
314 if (!found_vertex)
315 return Result("graphics pipeline requires a vertex shader");
316
317 for (const auto& att : color_attachments_) {
318 auto width = att.buffer->GetWidth();
319 auto height = att.buffer->GetHeight();
320 for (uint32_t level = 1; level < att.buffer->GetMipLevels(); level++) {
321 width >>= 1;
322 if (width == 0)
323 return Result("color attachment with " +
324 std::to_string(att.buffer->GetMipLevels()) +
325 " mip levels would have zero width for level " +
326 std::to_string(level));
327 height >>= 1;
328 if (height == 0)
329 return Result("color attachment with " +
330 std::to_string(att.buffer->GetMipLevels()) +
331 " mip levels would have zero height for level " +
332 std::to_string(level));
333 }
334 }
335
336 return {};
337 }
338
ValidateCompute() const339 Result Pipeline::ValidateCompute() const {
340 if (shaders_.empty())
341 return Result("compute pipeline requires a compute shader");
342
343 return {};
344 }
345
UpdateFramebufferSizes()346 void Pipeline::UpdateFramebufferSizes() {
347 uint32_t size = fb_width_ * fb_height_;
348 if (size == 0)
349 return;
350
351 for (auto& attachment : color_attachments_) {
352 auto mip0_width = fb_width_ << attachment.base_mip_level;
353 auto mip0_height = fb_height_ << attachment.base_mip_level;
354 attachment.buffer->SetWidth(mip0_width);
355 attachment.buffer->SetHeight(mip0_height);
356 attachment.buffer->SetElementCount(mip0_width * mip0_height);
357 }
358
359 if (depth_stencil_buffer_.buffer) {
360 depth_stencil_buffer_.buffer->SetWidth(fb_width_);
361 depth_stencil_buffer_.buffer->SetHeight(fb_height_);
362 depth_stencil_buffer_.buffer->SetElementCount(size);
363 }
364 }
365
AddColorAttachment(Buffer * buf,uint32_t location,uint32_t base_mip_level)366 Result Pipeline::AddColorAttachment(Buffer* buf,
367 uint32_t location,
368 uint32_t base_mip_level) {
369 for (const auto& attachment : color_attachments_) {
370 if (attachment.location == location)
371 return Result("can not bind two color buffers to the same LOCATION");
372 if (attachment.buffer == buf)
373 return Result("color buffer may only be bound to a PIPELINE once");
374 }
375
376 color_attachments_.push_back(BufferInfo{buf});
377
378 auto& info = color_attachments_.back();
379 info.location = location;
380 info.type = BufferType::kColor;
381 info.base_mip_level = base_mip_level;
382 auto mip0_width = fb_width_ << base_mip_level;
383 auto mip0_height = fb_height_ << base_mip_level;
384 buf->SetWidth(mip0_width);
385 buf->SetHeight(mip0_height);
386 buf->SetElementCount(mip0_width * mip0_height);
387
388 return {};
389 }
390
AddResolveTarget(Buffer * buf)391 Result Pipeline::AddResolveTarget(Buffer* buf) {
392 resolve_targets_.push_back(BufferInfo{buf});
393
394 auto& info = resolve_targets_.back();
395 info.type = BufferType::kResolve;
396 buf->SetWidth(fb_width_);
397 buf->SetHeight(fb_height_);
398 buf->SetElementCount(fb_width_ * fb_height_);
399
400 return {};
401 }
402
GetLocationForColorAttachment(Buffer * buf,uint32_t * loc) const403 Result Pipeline::GetLocationForColorAttachment(Buffer* buf,
404 uint32_t* loc) const {
405 for (const auto& info : color_attachments_) {
406 if (info.buffer == buf) {
407 *loc = info.location;
408 return {};
409 }
410 }
411 return Result("Unable to find requested buffer");
412 }
413
SetDepthStencilBuffer(Buffer * buf)414 Result Pipeline::SetDepthStencilBuffer(Buffer* buf) {
415 if (depth_stencil_buffer_.buffer != nullptr)
416 return Result("can only bind one depth/stencil buffer in a PIPELINE");
417
418 depth_stencil_buffer_.buffer = buf;
419 depth_stencil_buffer_.type = BufferType::kDepthStencil;
420
421 buf->SetWidth(fb_width_);
422 buf->SetHeight(fb_height_);
423 buf->SetElementCount(fb_width_ * fb_height_);
424 return {};
425 }
426
SetIndexBuffer(Buffer * buf)427 Result Pipeline::SetIndexBuffer(Buffer* buf) {
428 if (index_buffer_ != nullptr)
429 return Result("can only bind one INDEX_DATA buffer in a pipeline");
430
431 index_buffer_ = buf;
432 return {};
433 }
434
AddVertexBuffer(Buffer * buf,uint32_t location,InputRate rate,Format * format,uint32_t offset,uint32_t stride)435 Result Pipeline::AddVertexBuffer(Buffer* buf,
436 uint32_t location,
437 InputRate rate,
438 Format* format,
439 uint32_t offset,
440 uint32_t stride) {
441 for (const auto& vtex : vertex_buffers_) {
442 if (vtex.location == location)
443 return Result("can not bind two vertex buffers to the same LOCATION");
444 }
445
446 vertex_buffers_.push_back(BufferInfo{buf});
447 vertex_buffers_.back().location = location;
448 vertex_buffers_.back().type = BufferType::kVertex;
449 vertex_buffers_.back().input_rate = rate;
450 vertex_buffers_.back().format = format;
451 vertex_buffers_.back().offset = offset;
452 vertex_buffers_.back().stride = stride;
453 return {};
454 }
455
SetPushConstantBuffer(Buffer * buf)456 Result Pipeline::SetPushConstantBuffer(Buffer* buf) {
457 if (push_constant_buffer_.buffer != nullptr)
458 return Result("can only bind one push constant buffer in a PIPELINE");
459
460 push_constant_buffer_.buffer = buf;
461 push_constant_buffer_.type = BufferType::kPushConstant;
462 return {};
463 }
464
CreatePushConstantBuffer()465 Result Pipeline::CreatePushConstantBuffer() {
466 if (push_constant_buffer_.buffer != nullptr)
467 return Result("can only bind one push constant buffer in a PIPELINE");
468
469 TypeParser parser;
470 auto type = parser.Parse("R8_UINT");
471 auto fmt = MakeUnique<Format>(type.get());
472
473 std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
474 buf->SetName(kGeneratedPushConstantBuffer);
475 buf->SetFormat(fmt.get());
476
477 push_constant_buffer_.buffer = buf.get();
478 push_constant_buffer_.type = BufferType::kPushConstant;
479
480 formats_.push_back(std::move(fmt));
481 types_.push_back(std::move(type));
482 opencl_push_constants_ = std::move(buf);
483
484 return {};
485 }
486
GenerateDefaultColorAttachmentBuffer()487 std::unique_ptr<Buffer> Pipeline::GenerateDefaultColorAttachmentBuffer() {
488 TypeParser parser;
489 auto type = parser.Parse(kDefaultColorBufferFormat);
490 auto fmt = MakeUnique<Format>(type.get());
491
492 std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
493 buf->SetName(kGeneratedColorBuffer);
494 buf->SetFormat(fmt.get());
495
496 formats_.push_back(std::move(fmt));
497 types_.push_back(std::move(type));
498 return buf;
499 }
500
501 std::unique_ptr<Buffer>
GenerateDefaultDepthStencilAttachmentBuffer()502 Pipeline::GenerateDefaultDepthStencilAttachmentBuffer() {
503 TypeParser parser;
504 auto type = parser.Parse(kDefaultDepthBufferFormat);
505 auto fmt = MakeUnique<Format>(type.get());
506
507 std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
508 buf->SetName(kGeneratedDepthBuffer);
509 buf->SetFormat(fmt.get());
510
511 formats_.push_back(std::move(fmt));
512 types_.push_back(std::move(type));
513 return buf;
514 }
515
GetBufferForBinding(uint32_t descriptor_set,uint32_t binding) const516 Buffer* Pipeline::GetBufferForBinding(uint32_t descriptor_set,
517 uint32_t binding) const {
518 for (const auto& info : buffers_) {
519 if (info.descriptor_set == descriptor_set && info.binding == binding)
520 return info.buffer;
521 }
522 return nullptr;
523 }
524
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)525 void Pipeline::AddBuffer(Buffer* buf,
526 BufferType type,
527 uint32_t descriptor_set,
528 uint32_t binding,
529 uint32_t base_mip_level,
530 uint32_t dynamic_offset,
531 uint64_t descriptor_offset,
532 uint64_t descriptor_range) {
533 buffers_.push_back(BufferInfo{buf});
534
535 auto& info = buffers_.back();
536 info.descriptor_set = descriptor_set;
537 info.binding = binding;
538 info.type = type;
539 info.base_mip_level = base_mip_level;
540 info.dynamic_offset = dynamic_offset;
541 info.sampler = buf->GetSampler();
542 info.descriptor_offset = descriptor_offset;
543 info.descriptor_range = descriptor_range;
544 }
545
AddBuffer(Buffer * buf,BufferType type,const std::string & arg_name)546 void Pipeline::AddBuffer(Buffer* buf,
547 BufferType type,
548 const std::string& arg_name) {
549 // If this buffer binding already exists, overwrite with the new buffer.
550 for (auto& info : buffers_) {
551 if (info.arg_name == arg_name) {
552 info.buffer = buf;
553 return;
554 }
555 }
556
557 buffers_.push_back(BufferInfo{buf});
558
559 auto& info = buffers_.back();
560 info.type = type;
561 info.arg_name = arg_name;
562 info.descriptor_set = std::numeric_limits<uint32_t>::max();
563 info.binding = std::numeric_limits<uint32_t>::max();
564 info.arg_no = std::numeric_limits<uint32_t>::max();
565 info.base_mip_level = 0;
566 info.dynamic_offset = 0;
567 }
568
AddBuffer(Buffer * buf,BufferType type,uint32_t arg_no)569 void Pipeline::AddBuffer(Buffer* buf, BufferType type, uint32_t arg_no) {
570 // If this buffer binding already exists, overwrite with the new buffer.
571 for (auto& info : buffers_) {
572 if (info.arg_no == arg_no) {
573 info.buffer = buf;
574 return;
575 }
576 }
577
578 buffers_.push_back(BufferInfo{buf});
579
580 auto& info = buffers_.back();
581 info.type = type;
582 info.arg_no = arg_no;
583 info.descriptor_set = std::numeric_limits<uint32_t>::max();
584 info.binding = std::numeric_limits<uint32_t>::max();
585 info.base_mip_level = 0;
586 info.dynamic_offset = 0;
587 }
588
ClearBuffers(uint32_t descriptor_set,uint32_t binding)589 void Pipeline::ClearBuffers(uint32_t descriptor_set, uint32_t binding) {
590 buffers_.erase(
591 std::remove_if(buffers_.begin(), buffers_.end(),
592 [descriptor_set, binding](BufferInfo& info) -> bool {
593 return (info.descriptor_set == descriptor_set &&
594 info.binding == binding);
595 }),
596 buffers_.end());
597 }
598
AddSampler(Sampler * sampler,uint32_t descriptor_set,uint32_t binding)599 void Pipeline::AddSampler(Sampler* sampler,
600 uint32_t descriptor_set,
601 uint32_t binding) {
602 samplers_.push_back(SamplerInfo{sampler});
603
604 auto& info = samplers_.back();
605 info.descriptor_set = descriptor_set;
606 info.binding = binding;
607 info.mask = std::numeric_limits<uint32_t>::max();
608 }
609
AddSampler(Sampler * sampler,const std::string & arg_name)610 void Pipeline::AddSampler(Sampler* sampler, const std::string& arg_name) {
611 for (auto& info : samplers_) {
612 if (info.arg_name == arg_name) {
613 info.sampler = sampler;
614 return;
615 }
616 }
617
618 samplers_.push_back(SamplerInfo{sampler});
619
620 auto& info = samplers_.back();
621 info.arg_name = arg_name;
622 info.descriptor_set = std::numeric_limits<uint32_t>::max();
623 info.binding = std::numeric_limits<uint32_t>::max();
624 info.arg_no = std::numeric_limits<uint32_t>::max();
625 info.mask = std::numeric_limits<uint32_t>::max();
626 }
627
AddSampler(Sampler * sampler,uint32_t arg_no)628 void Pipeline::AddSampler(Sampler* sampler, uint32_t arg_no) {
629 for (auto& info : samplers_) {
630 if (info.arg_no == arg_no) {
631 info.sampler = sampler;
632 return;
633 }
634 }
635
636 samplers_.push_back(SamplerInfo{sampler});
637
638 auto& info = samplers_.back();
639 info.arg_no = arg_no;
640 info.descriptor_set = std::numeric_limits<uint32_t>::max();
641 info.binding = std::numeric_limits<uint32_t>::max();
642 info.mask = std::numeric_limits<uint32_t>::max();
643 }
644
AddSampler(uint32_t mask,uint32_t descriptor_set,uint32_t binding)645 void Pipeline::AddSampler(uint32_t mask,
646 uint32_t descriptor_set,
647 uint32_t binding) {
648 samplers_.push_back(SamplerInfo{nullptr});
649
650 auto& info = samplers_.back();
651 info.arg_name = "";
652 info.arg_no = std::numeric_limits<uint32_t>::max();
653 info.mask = mask;
654 info.descriptor_set = descriptor_set;
655 info.binding = binding;
656 }
657
ClearSamplers(uint32_t descriptor_set,uint32_t binding)658 void Pipeline::ClearSamplers(uint32_t descriptor_set, uint32_t binding) {
659 samplers_.erase(
660 std::remove_if(samplers_.begin(), samplers_.end(),
661 [descriptor_set, binding](SamplerInfo& info) -> bool {
662 return (info.descriptor_set == descriptor_set &&
663 info.binding == binding);
664 }),
665 samplers_.end());
666 }
667
UpdateOpenCLBufferBindings()668 Result Pipeline::UpdateOpenCLBufferBindings() {
669 if (!IsCompute() || GetShaders().empty() ||
670 GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
671 return {};
672 }
673
674 const auto& shader_info = GetShaders()[0];
675 const auto& descriptor_map = shader_info.GetDescriptorMap();
676 if (descriptor_map.empty())
677 return {};
678
679 const auto iter = descriptor_map.find(shader_info.GetEntryPoint());
680 if (iter == descriptor_map.end())
681 return {};
682
683 for (auto& info : samplers_) {
684 if (info.descriptor_set == std::numeric_limits<uint32_t>::max() &&
685 info.binding == std::numeric_limits<uint32_t>::max()) {
686 for (const auto& entry : iter->second) {
687 if (entry.arg_name == info.arg_name ||
688 entry.arg_ordinal == info.arg_no) {
689 if (entry.kind !=
690 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SAMPLER) {
691 return Result("Sampler bound to non-sampler kernel arg");
692 }
693 info.descriptor_set = entry.descriptor_set;
694 info.binding = entry.binding;
695 }
696 }
697 }
698 }
699
700 for (auto& info : buffers_) {
701 if (info.descriptor_set == std::numeric_limits<uint32_t>::max() &&
702 info.binding == std::numeric_limits<uint32_t>::max()) {
703 for (const auto& entry : iter->second) {
704 if (entry.arg_name == info.arg_name ||
705 entry.arg_ordinal == info.arg_no) {
706 // Buffer storage class consistency checks.
707 if (info.type == BufferType::kUnknown) {
708 // Set the appropriate buffer type.
709 switch (entry.kind) {
710 case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO:
711 case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO:
712 info.type = BufferType::kUniform;
713 break;
714 case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO:
715 case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD:
716 info.type = BufferType::kStorage;
717 break;
718 case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE:
719 info.type = BufferType::kSampledImage;
720 break;
721 case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE:
722 info.type = BufferType::kStorageImage;
723 break;
724 default:
725 return Result("Unhandled buffer type for OPENCL-C shader");
726 }
727 } else if (info.type == BufferType::kUniform) {
728 if (entry.kind !=
729 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO &&
730 entry.kind !=
731 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO) {
732 return Result("Buffer " + info.buffer->GetName() +
733 " must be a uniform binding");
734 }
735 } else if (info.type == BufferType::kStorage) {
736 if (entry.kind !=
737 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO &&
738 entry.kind !=
739 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD) {
740 return Result("Buffer " + info.buffer->GetName() +
741 " must be a storage binding");
742 }
743 } else if (info.type == BufferType::kSampledImage) {
744 if (entry.kind !=
745 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE) {
746 return Result("Buffer " + info.buffer->GetName() +
747 " must be a read-only image binding");
748 }
749 } else if (info.type == BufferType::kStorageImage) {
750 if (entry.kind !=
751 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE) {
752 return Result("Buffer " + info.buffer->GetName() +
753 " must be a write-only image binding");
754 }
755 } else {
756 return Result("Unhandled buffer type for OPENCL-C shader");
757 }
758 info.descriptor_set = entry.descriptor_set;
759 info.binding = entry.binding;
760 }
761 }
762 }
763 }
764
765 return {};
766 }
767
GenerateOpenCLPodBuffers()768 Result Pipeline::GenerateOpenCLPodBuffers() {
769 if (!IsCompute() || GetShaders().empty() ||
770 GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
771 return {};
772 }
773
774 const auto& shader_info = GetShaders()[0];
775 const auto& descriptor_map = shader_info.GetDescriptorMap();
776 if (descriptor_map.empty())
777 return {};
778
779 const auto iter = descriptor_map.find(shader_info.GetEntryPoint());
780 if (iter == descriptor_map.end())
781 return {};
782
783 // For each SET command, do the following:
784 // 1. Find the descriptor map entry for that argument.
785 // 2. Find or create the buffer for the descriptor set and binding pair.
786 // 3. Write the data for the SET command at the right offset.
787 for (const auto& arg_info : SetArgValues()) {
788 uint32_t descriptor_set = std::numeric_limits<uint32_t>::max();
789 uint32_t binding = std::numeric_limits<uint32_t>::max();
790 uint32_t offset = 0;
791 uint32_t arg_size = 0;
792 bool uses_name = !arg_info.name.empty();
793 Pipeline::ShaderInfo::DescriptorMapEntry::Kind kind =
794 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
795 for (const auto& entry : iter->second) {
796 if (entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD &&
797 entry.kind !=
798 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO &&
799 entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::
800 POD_PUSHCONSTANT) {
801 continue;
802 }
803
804 // Found the right entry.
805 if ((uses_name && entry.arg_name == arg_info.name) ||
806 entry.arg_ordinal == arg_info.ordinal) {
807 descriptor_set = entry.descriptor_set;
808 binding = entry.binding;
809 offset = entry.pod_offset;
810 arg_size = entry.pod_arg_size;
811 kind = entry.kind;
812 break;
813 }
814 }
815
816 Buffer* buffer = nullptr;
817 if (kind ==
818 Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT) {
819 if (GetPushConstantBuffer().buffer == nullptr) {
820 auto r = CreatePushConstantBuffer();
821 if (!r.IsSuccess())
822 return r;
823 }
824 buffer = GetPushConstantBuffer().buffer;
825 } else {
826 if (descriptor_set == std::numeric_limits<uint32_t>::max() ||
827 binding == std::numeric_limits<uint32_t>::max()) {
828 std::string message =
829 "could not find descriptor map entry for SET command: kernel " +
830 shader_info.GetEntryPoint();
831 if (uses_name) {
832 message += ", name " + arg_info.name;
833 } else {
834 message += ", number " + std::to_string(arg_info.ordinal);
835 }
836 return Result(message);
837 }
838
839 auto buf_iter = opencl_pod_buffer_map_.lower_bound(
840 std::make_pair(descriptor_set, binding));
841 if (buf_iter == opencl_pod_buffer_map_.end() ||
842 buf_iter->first.first != descriptor_set ||
843 buf_iter->first.second != binding) {
844 // Ensure no buffer was previously bound for this descriptor set and
845 // binding pair.
846 for (const auto& buf_info : GetBuffers()) {
847 if (buf_info.descriptor_set == descriptor_set &&
848 buf_info.binding == binding) {
849 return Result("previously bound buffer " +
850 buf_info.buffer->GetName() +
851 " to PoD args at descriptor set " +
852 std::to_string(descriptor_set) + " binding " +
853 std::to_string(binding));
854 }
855 }
856
857 // Add a new buffer for this descriptor set and binding.
858 opencl_pod_buffers_.push_back(MakeUnique<Buffer>());
859 buffer = opencl_pod_buffers_.back().get();
860 auto buffer_type =
861 kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD
862 ? BufferType::kStorage
863 : BufferType::kUniform;
864
865 // Use an 8-bit type because all the data in the descriptor map is
866 // byte-based and it simplifies the logic for sizing below.
867 TypeParser parser;
868 auto type = parser.Parse("R8_UINT");
869 auto fmt = MakeUnique<Format>(type.get());
870 buffer->SetFormat(fmt.get());
871 formats_.push_back(std::move(fmt));
872 types_.push_back(std::move(type));
873
874 buffer->SetName(GetName() + "_pod_buffer_" +
875 std::to_string(descriptor_set) + "_" +
876 std::to_string(binding));
877 opencl_pod_buffer_map_.insert(
878 buf_iter,
879 std::make_pair(std::make_pair(descriptor_set, binding), buffer));
880 AddBuffer(buffer, buffer_type, descriptor_set, binding, 0, 0, 0, ~0ULL);
881 } else {
882 buffer = buf_iter->second;
883 }
884
885 // Resize if necessary.
886 if (buffer->ValueCount() < offset + arg_size) {
887 buffer->SetSizeInElements(offset + arg_size);
888 }
889
890 // Check the data size.
891 if (arg_size != arg_info.fmt->SizeInBytes()) {
892 std::string message = "SET command uses incorrect data size: kernel " +
893 shader_info.GetEntryPoint();
894 if (uses_name) {
895 message += ", name " + arg_info.name;
896 } else {
897 message += ", number " + std::to_string(arg_info.ordinal);
898 }
899 return Result(message);
900 }
901 }
902
903 // Convert the argument value into bytes. Currently, only scalar arguments
904 // are supported.
905 const auto arg_byte_size = arg_info.fmt->SizeInBytes();
906 std::vector<Value> data_bytes;
907 for (uint32_t i = 0; i < arg_byte_size; ++i) {
908 Value v;
909 if (arg_info.value.IsFloat()) {
910 if (arg_byte_size == sizeof(double)) {
911 union {
912 uint64_t u;
913 double d;
914 } u;
915 u.d = arg_info.value.AsDouble();
916 v.SetIntValue((u.u >> (i * 8)) & 0xff);
917 } else {
918 union {
919 uint32_t u;
920 float f;
921 } u;
922 u.f = arg_info.value.AsFloat();
923 v.SetIntValue((u.u >> (i * 8)) & 0xff);
924 }
925 } else {
926 v.SetIntValue((arg_info.value.AsUint64() >> (i * 8)) & 0xff);
927 }
928 data_bytes.push_back(v);
929 }
930 Result r = buffer->SetDataWithOffset(data_bytes, offset);
931 if (!r.IsSuccess())
932 return r;
933 }
934
935 return {};
936 }
937
GenerateOpenCLLiteralSamplers()938 Result Pipeline::GenerateOpenCLLiteralSamplers() {
939 for (auto& info : samplers_) {
940 if (info.sampler || info.mask == std::numeric_limits<uint32_t>::max())
941 continue;
942
943 auto literal_sampler = MakeUnique<Sampler>();
944 literal_sampler->SetName("literal." + std::to_string(info.descriptor_set) +
945 "." + std::to_string(info.binding));
946
947 // The values for addressing modes, filtering modes and coordinate
948 // normalization are all defined in the OpenCL header.
949
950 literal_sampler->SetNormalizedCoords(info.mask &
951 kOpenCLNormalizedCoordsBit);
952
953 uint32_t addressing_bits = info.mask & kOpenCLAddressModeBits;
954 AddressMode addressing_mode = AddressMode::kUnknown;
955 if (addressing_bits == kOpenCLAddressModeNone ||
956 addressing_bits == kOpenCLAddressModeClampToEdge) {
957 // CLK_ADDRESS_NONE
958 // CLK_ADDERSS_CLAMP_TO_EDGE
959 addressing_mode = AddressMode::kClampToEdge;
960 } else if (addressing_bits == kOpenCLAddressModeClamp) {
961 // CLK_ADDRESS_CLAMP
962 addressing_mode = AddressMode::kClampToBorder;
963 } else if (addressing_bits == kOpenCLAddressModeRepeat) {
964 // CLK_ADDRESS_REPEAT
965 addressing_mode = AddressMode::kRepeat;
966 } else if (addressing_bits == kOpenCLAddressModeMirroredRepeat) {
967 // CLK_ADDRESS_MIRRORED_REPEAT
968 addressing_mode = AddressMode::kMirroredRepeat;
969 }
970 literal_sampler->SetAddressModeU(addressing_mode);
971 literal_sampler->SetAddressModeV(addressing_mode);
972 // TODO(alan-baker): If this is used with an arrayed image then W should use
973 // kClampToEdge always, but this information is not currently available.
974 literal_sampler->SetAddressModeW(addressing_mode);
975
976 // Next bit is filtering mode.
977 FilterType filtering_mode = FilterType::kUnknown;
978 if (info.mask & kOpenCLFilterModeNearestBit) {
979 filtering_mode = FilterType::kNearest;
980 } else if (info.mask & kOpenCLFilterModeLinearBit) {
981 filtering_mode = FilterType::kLinear;
982 }
983 literal_sampler->SetMagFilter(filtering_mode);
984 literal_sampler->SetMinFilter(filtering_mode);
985
986 // TODO(alan-baker): OpenCL wants the border color to be based on image
987 // channel orders which aren't accessible.
988
989 // clspv never generates multiple MIPMAP levels.
990 literal_sampler->SetMinLOD(0.0f);
991 literal_sampler->SetMaxLOD(0.0f);
992
993 opencl_literal_samplers_.push_back(std::move(literal_sampler));
994 info.sampler = opencl_literal_samplers_.back().get();
995 }
996
997 return {};
998 }
999
GenerateOpenCLPushConstants()1000 Result Pipeline::GenerateOpenCLPushConstants() {
1001 if (!IsCompute() || GetShaders().empty() ||
1002 GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
1003 return {};
1004 }
1005
1006 const auto& shader_info = GetShaders()[0];
1007 if (shader_info.GetPushConstants().empty())
1008 return {};
1009
1010 Result r = CreatePushConstantBuffer();
1011 if (!r.IsSuccess())
1012 return r;
1013
1014 auto* buf = GetPushConstantBuffer().buffer;
1015 assert(buf);
1016
1017 // Determine size and contents of the push constant buffer.
1018 for (const auto& pc : shader_info.GetPushConstants()) {
1019 assert(pc.size % sizeof(uint32_t) == 0);
1020 assert(pc.offset % sizeof(uint32_t) == 0);
1021
1022 if (buf->GetSizeInBytes() < pc.offset + pc.size)
1023 buf->SetSizeInBytes(pc.offset + pc.size);
1024
1025 std::vector<uint32_t> bytes(pc.size / sizeof(uint32_t));
1026 uint32_t base = 0;
1027 switch (pc.type) {
1028 case Pipeline::ShaderInfo::PushConstant::PushConstantType::kDimensions:
1029 // All compute kernel launches are 3D.
1030 bytes[base] = 3;
1031 break;
1032 case Pipeline::ShaderInfo::PushConstant::PushConstantType::kGlobalOffset:
1033 // Global offsets are not currently supported.
1034 bytes[base] = 0;
1035 bytes[base + 1] = 0;
1036 bytes[base + 2] = 0;
1037 break;
1038 case Pipeline::ShaderInfo::PushConstant::PushConstantType::kRegionOffset:
1039 // Region offsets are not currently supported.
1040 bytes[base] = 0;
1041 bytes[base + 1] = 0;
1042 bytes[base + 2] = 0;
1043 break;
1044 }
1045 memcpy(buf->ValuePtr()->data() + pc.offset, bytes.data(),
1046 bytes.size() * sizeof(uint32_t));
1047 }
1048
1049 return {};
1050 }
1051
1052 } // namespace amber
1053