• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/vulkan/engine_vulkan.h"
16 
17 #include <algorithm>
18 #include <set>
19 #include <utility>
20 
21 #include "amber/amber_vulkan.h"
22 #include "src/make_unique.h"
23 #include "src/type_parser.h"
24 #include "src/vulkan/compute_pipeline.h"
25 #include "src/vulkan/graphics_pipeline.h"
26 
27 namespace amber {
28 namespace vulkan {
29 namespace {
30 
ToVkShaderStage(ShaderType type,VkShaderStageFlagBits * ret)31 Result ToVkShaderStage(ShaderType type, VkShaderStageFlagBits* ret) {
32   switch (type) {
33     case kShaderTypeGeometry:
34       *ret = VK_SHADER_STAGE_GEOMETRY_BIT;
35       break;
36     case kShaderTypeFragment:
37       *ret = VK_SHADER_STAGE_FRAGMENT_BIT;
38       break;
39     case kShaderTypeVertex:
40       *ret = VK_SHADER_STAGE_VERTEX_BIT;
41       break;
42     case kShaderTypeTessellationControl:
43       *ret = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
44       break;
45     case kShaderTypeTessellationEvaluation:
46       *ret = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
47       break;
48     case kShaderTypeCompute:
49       *ret = VK_SHADER_STAGE_COMPUTE_BIT;
50       break;
51     case kShaderTypeMulti:
52       *ret = VK_SHADER_STAGE_FRAGMENT_BIT;
53       return Result("Vulkan::Unknown shader stage");
54   }
55 
56   return {};
57 }
58 
AreAllExtensionsSupported(const std::vector<std::string> & available_extensions,const std::vector<std::string> & required_extensions)59 bool AreAllExtensionsSupported(
60     const std::vector<std::string>& available_extensions,
61     const std::vector<std::string>& required_extensions) {
62   if (required_extensions.empty())
63     return true;
64 
65   std::set<std::string> required_extension_set(required_extensions.begin(),
66                                                required_extensions.end());
67   for (const auto& extension : available_extensions) {
68     required_extension_set.erase(extension);
69   }
70 
71   return required_extension_set.empty();
72 }
73 
74 }  // namespace
75 
EngineVulkan()76 EngineVulkan::EngineVulkan() : Engine() {}
77 
~EngineVulkan()78 EngineVulkan::~EngineVulkan() {
79   for (auto it = pipeline_map_.begin(); it != pipeline_map_.end(); ++it) {
80     auto& info = it->second;
81 
82     for (auto mod_it = info.shader_info.begin();
83          mod_it != info.shader_info.end(); ++mod_it) {
84       auto vk_device = device_->GetVkDevice();
85       if (vk_device != VK_NULL_HANDLE &&
86           mod_it->second.shader != VK_NULL_HANDLE) {
87         device_->GetPtrs()->vkDestroyShaderModule(
88             vk_device, mod_it->second.shader, nullptr);
89       }
90     }
91   }
92 }
93 
Initialize(EngineConfig * config,Delegate * delegate,const std::vector<std::string> & features,const std::vector<std::string> & instance_extensions,const std::vector<std::string> & device_extensions)94 Result EngineVulkan::Initialize(
95     EngineConfig* config,
96     Delegate* delegate,
97     const std::vector<std::string>& features,
98     const std::vector<std::string>& instance_extensions,
99     const std::vector<std::string>& device_extensions) {
100   if (device_)
101     return Result("Vulkan::Initialize device_ already exists");
102 
103   VulkanEngineConfig* vk_config = static_cast<VulkanEngineConfig*>(config);
104   if (!vk_config || vk_config->vkGetInstanceProcAddr == VK_NULL_HANDLE)
105     return Result("Vulkan::Initialize vkGetInstanceProcAddr must be provided.");
106   if (vk_config->device == VK_NULL_HANDLE)
107     return Result("Vulkan::Initialize device must be provided");
108   if (vk_config->physical_device == VK_NULL_HANDLE)
109     return Result("Vulkan::Initialize physical device handle is null.");
110   if (vk_config->queue == VK_NULL_HANDLE)
111     return Result("Vulkan::Initialize queue handle is null.");
112 
113   // Validate instance extensions
114   if (!AreAllExtensionsSupported(vk_config->available_instance_extensions,
115                                  instance_extensions)) {
116     return Result("Vulkan::Initialize not all instance extensions supported");
117   }
118 
119   device_ = MakeUnique<Device>(vk_config->instance, vk_config->physical_device,
120                                vk_config->queue_family_index, vk_config->device,
121                                vk_config->queue);
122 
123   Result r = device_->Initialize(
124       vk_config->vkGetInstanceProcAddr, delegate, features, device_extensions,
125       vk_config->available_features, vk_config->available_features2,
126       vk_config->available_device_extensions);
127   if (!r.IsSuccess())
128     return r;
129 
130   if (!pool_) {
131     pool_ = MakeUnique<CommandPool>(device_.get());
132     r = pool_->Initialize();
133     if (!r.IsSuccess())
134       return r;
135   }
136 
137   return {};
138 }
139 
CreatePipeline(amber::Pipeline * pipeline)140 Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
141   // Create the pipeline data early so we can access them as needed.
142   pipeline_map_[pipeline] = PipelineInfo();
143   auto& info = pipeline_map_[pipeline];
144 
145   for (const auto& shader_info : pipeline->GetShaders()) {
146     Result r =
147         SetShader(pipeline, shader_info.GetShaderType(), shader_info.GetData());
148     if (!r.IsSuccess())
149       return r;
150   }
151 
152   for (const auto& colour_info : pipeline->GetColorAttachments()) {
153     auto fmt = colour_info.buffer->GetFormat();
154     if (!device_->IsFormatSupportedByPhysicalDevice(*fmt, colour_info.buffer))
155       return Result("Vulkan color attachment format is not supported");
156   }
157 
158   Format* depth_fmt = nullptr;
159   if (pipeline->GetDepthBuffer().buffer) {
160     const auto& depth_info = pipeline->GetDepthBuffer();
161 
162     depth_fmt = depth_info.buffer->GetFormat();
163     if (!device_->IsFormatSupportedByPhysicalDevice(*depth_fmt,
164                                                     depth_info.buffer)) {
165       return Result("Vulkan depth attachment format is not supported");
166     }
167   }
168 
169   std::vector<VkPipelineShaderStageCreateInfo> stage_create_info;
170   Result r = GetVkShaderStageInfo(pipeline, &stage_create_info);
171   if (!r.IsSuccess())
172     return r;
173 
174   const auto& engine_data = GetEngineData();
175   std::unique_ptr<Pipeline> vk_pipeline;
176   if (pipeline->GetType() == PipelineType::kCompute) {
177     vk_pipeline = MakeUnique<ComputePipeline>(
178         device_.get(), engine_data.fence_timeout_ms, stage_create_info);
179     r = vk_pipeline->AsCompute()->Initialize(pool_.get());
180     if (!r.IsSuccess())
181       return r;
182   } else {
183     vk_pipeline = MakeUnique<GraphicsPipeline>(
184         device_.get(), pipeline->GetColorAttachments(), depth_fmt,
185         engine_data.fence_timeout_ms, stage_create_info);
186 
187     r = vk_pipeline->AsGraphics()->Initialize(pipeline->GetFramebufferWidth(),
188                                               pipeline->GetFramebufferHeight(),
189                                               pool_.get());
190     if (!r.IsSuccess())
191       return r;
192   }
193 
194   info.vk_pipeline = std::move(vk_pipeline);
195 
196   // Set the entry point names for the pipeline.
197   for (const auto& shader_info : pipeline->GetShaders()) {
198     VkShaderStageFlagBits stage = VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;
199     r = ToVkShaderStage(shader_info.GetShaderType(), &stage);
200     if (!r.IsSuccess())
201       return r;
202     const auto& name = shader_info.GetEntryPoint();
203     if (!name.empty()) {
204       info.vk_pipeline->SetEntryPointName(stage, name);
205     }
206   }
207 
208   for (const auto& vtex_info : pipeline->GetVertexBuffers()) {
209     auto fmt = vtex_info.buffer->GetFormat();
210     if (!device_->IsFormatSupportedByPhysicalDevice(*fmt, vtex_info.buffer))
211       return Result("Vulkan vertex buffer format is not supported");
212     if (!info.vertex_buffer)
213       info.vertex_buffer = MakeUnique<VertexBuffer>(device_.get());
214 
215     info.vertex_buffer->SetData(static_cast<uint8_t>(vtex_info.location),
216                                 vtex_info.buffer);
217   }
218 
219   if (pipeline->GetIndexBuffer()) {
220     auto* buf = pipeline->GetIndexBuffer();
221     info.vk_pipeline->AsGraphics()->SetIndexBuffer(buf);
222   }
223 
224   if (pipeline->GetPushConstantBuffer().buffer != nullptr) {
225     r = info.vk_pipeline->AddPushConstantBuffer(
226         pipeline->GetPushConstantBuffer().buffer, 0);
227     if (!r.IsSuccess())
228       return r;
229   }
230 
231   for (const auto& buf_info : pipeline->GetBuffers()) {
232     auto type = BufferCommand::BufferType::kSSBO;
233     if (buf_info.buffer->GetBufferType() == BufferType::kUniform) {
234       type = BufferCommand::BufferType::kUniform;
235     } else if (buf_info.buffer->GetBufferType() != BufferType::kStorage) {
236       return Result("Vulkan: CreatePipeline - unknown buffer type: " +
237                     std::to_string(static_cast<uint32_t>(
238                         buf_info.buffer->GetBufferType())));
239     }
240 
241     auto cmd = MakeUnique<BufferCommand>(type, pipeline);
242     cmd->SetDescriptorSet(buf_info.descriptor_set);
243     cmd->SetBinding(buf_info.binding);
244     cmd->SetBuffer(buf_info.buffer);
245 
246     r = info.vk_pipeline->AddDescriptor(cmd.get());
247     if (!r.IsSuccess())
248       return r;
249   }
250 
251   return {};
252 }
253 
SetShader(amber::Pipeline * pipeline,ShaderType type,const std::vector<uint32_t> & data)254 Result EngineVulkan::SetShader(amber::Pipeline* pipeline,
255                                ShaderType type,
256                                const std::vector<uint32_t>& data) {
257   auto& info = pipeline_map_[pipeline];
258 
259   auto it = info.shader_info.find(type);
260   if (it != info.shader_info.end())
261     return Result("Vulkan::Setting Duplicated Shader Types Fail");
262 
263   VkShaderModuleCreateInfo create_info = VkShaderModuleCreateInfo();
264   create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
265   create_info.codeSize = data.size() * sizeof(uint32_t);
266   create_info.pCode = data.data();
267 
268   VkShaderModule shader;
269   if (device_->GetPtrs()->vkCreateShaderModule(device_->GetVkDevice(),
270                                                &create_info, nullptr,
271                                                &shader) != VK_SUCCESS) {
272     return Result("Vulkan::Calling vkCreateShaderModule Fail");
273   }
274 
275   info.shader_info[type].shader = shader;
276 
277   for (auto& shader_info : pipeline->GetShaders()) {
278     if (shader_info.GetShaderType() != type)
279       continue;
280 
281     const auto& shader_spec_info = shader_info.GetSpecialization();
282     if (shader_spec_info.empty())
283       continue;
284 
285     auto& entries = info.shader_info[type].specialization_entries;
286     entries.reset(new std::vector<VkSpecializationMapEntry>());
287     auto& entry_data = info.shader_info[type].specialization_data;
288     entry_data.reset(new std::vector<uint32_t>());
289     uint32_t i = 0;
290     for (auto pair : shader_spec_info) {
291       entries->push_back({pair.first,
292                           static_cast<uint32_t>(i * sizeof(uint32_t)),
293                           static_cast<uint32_t>(sizeof(uint32_t))});
294       entry_data->push_back(pair.second);
295       ++i;
296     }
297     auto& spec_info = info.shader_info[type].specialization_info;
298     spec_info.reset(new VkSpecializationInfo());
299     spec_info->mapEntryCount = static_cast<uint32_t>(shader_spec_info.size());
300     spec_info->pMapEntries = entries->data();
301     spec_info->dataSize = sizeof(uint32_t) * shader_spec_info.size();
302     spec_info->pData = entry_data->data();
303   }
304 
305   return {};
306 }
307 
GetVkShaderStageInfo(amber::Pipeline * pipeline,std::vector<VkPipelineShaderStageCreateInfo> * out)308 Result EngineVulkan::GetVkShaderStageInfo(
309     amber::Pipeline* pipeline,
310     std::vector<VkPipelineShaderStageCreateInfo>* out) {
311   auto& info = pipeline_map_[pipeline];
312 
313   std::vector<VkPipelineShaderStageCreateInfo> stage_info(
314       info.shader_info.size());
315   uint32_t stage_count = 0;
316   for (auto& it : info.shader_info) {
317     VkShaderStageFlagBits stage = VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;
318     Result r = ToVkShaderStage(it.first, &stage);
319     if (!r.IsSuccess())
320       return r;
321 
322     stage_info[stage_count] = VkPipelineShaderStageCreateInfo();
323     stage_info[stage_count].sType =
324         VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
325     stage_info[stage_count].stage = stage;
326     stage_info[stage_count].module = it.second.shader;
327     stage_info[stage_count].pName = nullptr;
328     if (it.second.specialization_entries &&
329         !it.second.specialization_entries->empty()) {
330       stage_info[stage_count].pSpecializationInfo =
331           it.second.specialization_info.get();
332     }
333     ++stage_count;
334   }
335   *out = stage_info;
336   return {};
337 }
338 
DoClearColor(const ClearColorCommand * command)339 Result EngineVulkan::DoClearColor(const ClearColorCommand* command) {
340   auto& info = pipeline_map_[command->GetPipeline()];
341   if (!info.vk_pipeline->IsGraphics())
342     return Result("Vulkan::Clear Color Command for Non-Graphics Pipeline");
343 
344   return info.vk_pipeline->AsGraphics()->SetClearColor(
345       command->GetR(), command->GetG(), command->GetB(), command->GetA());
346 }
347 
DoClearStencil(const ClearStencilCommand * command)348 Result EngineVulkan::DoClearStencil(const ClearStencilCommand* command) {
349   auto& info = pipeline_map_[command->GetPipeline()];
350   if (!info.vk_pipeline->IsGraphics())
351     return Result("Vulkan::Clear Stencil Command for Non-Graphics Pipeline");
352 
353   return info.vk_pipeline->AsGraphics()->SetClearStencil(command->GetValue());
354 }
355 
DoClearDepth(const ClearDepthCommand * command)356 Result EngineVulkan::DoClearDepth(const ClearDepthCommand* command) {
357   auto& info = pipeline_map_[command->GetPipeline()];
358   if (!info.vk_pipeline->IsGraphics())
359     return Result("Vulkan::Clear Depth Command for Non-Graphics Pipeline");
360 
361   return info.vk_pipeline->AsGraphics()->SetClearDepth(command->GetValue());
362 }
363 
DoClear(const ClearCommand * command)364 Result EngineVulkan::DoClear(const ClearCommand* command) {
365   auto& info = pipeline_map_[command->GetPipeline()];
366   if (!info.vk_pipeline->IsGraphics())
367     return Result("Vulkan::Clear Command for Non-Graphics Pipeline");
368 
369   return info.vk_pipeline->AsGraphics()->Clear();
370 }
371 
DoDrawRect(const DrawRectCommand * command)372 Result EngineVulkan::DoDrawRect(const DrawRectCommand* command) {
373   auto& info = pipeline_map_[command->GetPipeline()];
374   if (!info.vk_pipeline->IsGraphics())
375     return Result("Vulkan::DrawRect for Non-Graphics Pipeline");
376 
377   auto* graphics = info.vk_pipeline->AsGraphics();
378 
379   float x = command->GetX();
380   float y = command->GetY();
381   float width = command->GetWidth();
382   float height = command->GetHeight();
383 
384   if (command->IsOrtho()) {
385     const float frame_width = static_cast<float>(graphics->GetWidth());
386     const float frame_height = static_cast<float>(graphics->GetHeight());
387     x = ((x / frame_width) * 2.0f) - 1.0f;
388     y = ((y / frame_height) * 2.0f) - 1.0f;
389     width = (width / frame_width) * 2.0f;
390     height = (height / frame_height) * 2.0f;
391   }
392 
393   std::vector<Value> values(8);
394   // Bottom left
395   values[0].SetDoubleValue(static_cast<double>(x));
396   values[1].SetDoubleValue(static_cast<double>(y + height));
397   // Top left
398   values[2].SetDoubleValue(static_cast<double>(x));
399   values[3].SetDoubleValue(static_cast<double>(y));
400   // Bottom right
401   values[4].SetDoubleValue(static_cast<double>(x + width));
402   values[5].SetDoubleValue(static_cast<double>(y + height));
403   // Top right
404   values[6].SetDoubleValue(static_cast<double>(x + width));
405   values[7].SetDoubleValue(static_cast<double>(y));
406 
407   // |format| is not Format for frame buffer but for vertex buffer.
408   // Since draw rect command contains its vertex information and it
409   // does not include a format of vertex buffer, we can choose any
410   // one that is suitable. We use VK_FORMAT_R32G32_SFLOAT for it.
411   TypeParser parser;
412   auto type = parser.Parse("R32G32_SFLOAT");
413   Format fmt(type.get());
414 
415   auto buf = MakeUnique<Buffer>();
416   buf->SetFormat(&fmt);
417   buf->SetData(std::move(values));
418 
419   auto vertex_buffer = MakeUnique<VertexBuffer>(device_.get());
420   vertex_buffer->SetData(0, buf.get());
421 
422   DrawArraysCommand draw(command->GetPipeline(), *command->GetPipelineData());
423   draw.SetTopology(command->IsPatch() ? Topology::kPatchList
424                                       : Topology::kTriangleStrip);
425   draw.SetFirstVertexIndex(0);
426   draw.SetVertexCount(4);
427   draw.SetInstanceCount(1);
428 
429   Result r = graphics->Draw(&draw, vertex_buffer.get());
430   if (!r.IsSuccess())
431     return r;
432 
433   return {};
434 }
435 
DoDrawArrays(const DrawArraysCommand * command)436 Result EngineVulkan::DoDrawArrays(const DrawArraysCommand* command) {
437   auto& info = pipeline_map_[command->GetPipeline()];
438   if (!info.vk_pipeline)
439     return Result("Vulkan::DrawArrays for Non-Graphics Pipeline");
440 
441   return info.vk_pipeline->AsGraphics()->Draw(command,
442                                               info.vertex_buffer.get());
443 }
444 
DoCompute(const ComputeCommand * command)445 Result EngineVulkan::DoCompute(const ComputeCommand* command) {
446   auto& info = pipeline_map_[command->GetPipeline()];
447   if (info.vk_pipeline->IsGraphics())
448     return Result("Vulkan: Compute called for graphics pipeline.");
449 
450   return info.vk_pipeline->AsCompute()->Compute(
451       command->GetX(), command->GetY(), command->GetZ());
452 }
453 
DoEntryPoint(const EntryPointCommand * command)454 Result EngineVulkan::DoEntryPoint(const EntryPointCommand* command) {
455   auto& info = pipeline_map_[command->GetPipeline()];
456   if (!info.vk_pipeline)
457     return Result("Vulkan::DoEntryPoint no Pipeline exists");
458 
459   VkShaderStageFlagBits stage = VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;
460   Result r = ToVkShaderStage(command->GetShaderType(), &stage);
461   if (!r.IsSuccess())
462     return r;
463 
464   info.vk_pipeline->SetEntryPointName(stage, command->GetEntryPointName());
465   return {};
466 }
467 
DoPatchParameterVertices(const PatchParameterVerticesCommand * command)468 Result EngineVulkan::DoPatchParameterVertices(
469     const PatchParameterVerticesCommand* command) {
470   auto& info = pipeline_map_[command->GetPipeline()];
471   if (!info.vk_pipeline->IsGraphics())
472     return Result("Vulkan::DoPatchParameterVertices for Non-Graphics Pipeline");
473 
474   info.vk_pipeline->AsGraphics()->SetPatchControlPoints(
475       command->GetControlPointCount());
476   return {};
477 }
478 
DoBuffer(const BufferCommand * cmd)479 Result EngineVulkan::DoBuffer(const BufferCommand* cmd) {
480   if (!device_->IsDescriptorSetInBounds(cmd->GetDescriptorSet())) {
481     return Result(
482         "Vulkan::DoBuffer exceed maxBoundDescriptorSets limit of physical "
483         "device");
484   }
485   auto& info = pipeline_map_[cmd->GetPipeline()];
486   return info.vk_pipeline->AddDescriptor(cmd);
487 }
488 
489 }  // namespace vulkan
490 }  // namespace amber
491