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