1 // Copyright 2019 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/command_buffer.h"
16
17 #include <cassert>
18 #include <string>
19
20 #include "src/vulkan/command_pool.h"
21 #include "src/vulkan/device.h"
22
23 namespace amber {
24 namespace vulkan {
25
CommandBuffer(Device * device,CommandPool * pool)26 CommandBuffer::CommandBuffer(Device* device, CommandPool* pool)
27 : device_(device), pool_(pool) {}
28
~CommandBuffer()29 CommandBuffer::~CommandBuffer() {
30 Reset();
31
32 if (fence_ != VK_NULL_HANDLE)
33 device_->GetPtrs()->vkDestroyFence(device_->GetVkDevice(), fence_, nullptr);
34
35 if (command_ != VK_NULL_HANDLE) {
36 device_->GetPtrs()->vkFreeCommandBuffers(
37 device_->GetVkDevice(), pool_->GetVkCommandPool(), 1, &command_);
38 }
39 }
40
Initialize()41 Result CommandBuffer::Initialize() {
42 VkCommandBufferAllocateInfo command_info = VkCommandBufferAllocateInfo();
43 command_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
44 command_info.commandPool = pool_->GetVkCommandPool();
45 command_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
46 command_info.commandBufferCount = 1;
47
48 if (device_->GetPtrs()->vkAllocateCommandBuffers(
49 device_->GetVkDevice(), &command_info, &command_) != VK_SUCCESS) {
50 return Result("Vulkan::Calling vkAllocateCommandBuffers Fail");
51 }
52
53 VkFenceCreateInfo fence_info = VkFenceCreateInfo();
54 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
55 if (device_->GetPtrs()->vkCreateFence(device_->GetVkDevice(), &fence_info,
56 nullptr, &fence_) != VK_SUCCESS) {
57 return Result("Vulkan::Calling vkCreateFence Fail");
58 }
59
60 return {};
61 }
62
BeginRecording()63 Result CommandBuffer::BeginRecording() {
64 VkCommandBufferBeginInfo command_begin_info = VkCommandBufferBeginInfo();
65 command_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
66 command_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
67 if (device_->GetPtrs()->vkBeginCommandBuffer(command_, &command_begin_info) !=
68 VK_SUCCESS) {
69 return Result("Vulkan::Calling vkBeginCommandBuffer Fail");
70 }
71 guarded_ = true;
72
73 return {};
74 }
75
SubmitAndReset(uint32_t timeout_ms)76 Result CommandBuffer::SubmitAndReset(uint32_t timeout_ms) {
77 if (device_->GetPtrs()->vkEndCommandBuffer(command_) != VK_SUCCESS)
78 return Result("Vulkan::Calling vkEndCommandBuffer Fail");
79
80 if (device_->GetPtrs()->vkResetFences(device_->GetVkDevice(), 1, &fence_) !=
81 VK_SUCCESS) {
82 return Result("Vulkan::Calling vkResetFences Fail");
83 }
84
85 VkSubmitInfo submit_info = VkSubmitInfo();
86 submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
87 submit_info.commandBufferCount = 1;
88 submit_info.pCommandBuffers = &command_;
89 if (device_->GetPtrs()->vkQueueSubmit(device_->GetVkQueue(), 1, &submit_info,
90 fence_) != VK_SUCCESS) {
91 return Result("Vulkan::Calling vkQueueSubmit Fail");
92 }
93
94 guarded_ = false;
95
96 VkResult r = device_->GetPtrs()->vkWaitForFences(
97 device_->GetVkDevice(), 1, &fence_, VK_TRUE,
98 static_cast<uint64_t>(timeout_ms) * 1000ULL * 1000ULL /* nanosecond */);
99 if (r == VK_TIMEOUT)
100 return Result("Vulkan::Calling vkWaitForFences Timeout");
101 if (r != VK_SUCCESS) {
102 std::string result_str;
103 switch (r) {
104 case VK_ERROR_OUT_OF_HOST_MEMORY:
105 result_str = "OUT_OF_HOST_MEMORY";
106 break;
107 case VK_ERROR_OUT_OF_DEVICE_MEMORY:
108 result_str = "OUT_OF_DEVICE_MEMORY";
109 break;
110 case VK_ERROR_DEVICE_LOST:
111 result_str = "DEVICE_LOST";
112 break;
113 default:
114 result_str = "<UNEXPECTED RESULT>";
115 break;
116 }
117 return Result("Vulkan::Calling vkWaitForFences Fail (" + result_str + ")");
118 }
119
120 if (device_->GetPtrs()->vkResetCommandBuffer(command_, 0) != VK_SUCCESS)
121 return Result("Vulkan::Calling vkResetCommandBuffer Fail");
122
123 return {};
124 }
125
Reset()126 void CommandBuffer::Reset() {
127 if (guarded_) {
128 device_->GetPtrs()->vkResetCommandBuffer(command_, 0);
129 guarded_ = false;
130 }
131 }
132
CommandBufferGuard(CommandBuffer * buffer)133 CommandBufferGuard::CommandBufferGuard(CommandBuffer* buffer)
134 : buffer_(buffer) {
135 assert(!buffer_->guarded_);
136 result_ = buffer_->BeginRecording();
137 }
138
~CommandBufferGuard()139 CommandBufferGuard::~CommandBufferGuard() {
140 if (buffer_->guarded_)
141 buffer_->Reset();
142 }
143
Submit(uint32_t timeout_ms)144 Result CommandBufferGuard::Submit(uint32_t timeout_ms) {
145 assert(buffer_->guarded_);
146 return buffer_->SubmitAndReset(timeout_ms);
147 }
148
149 } // namespace vulkan
150 } // namespace amber
151