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,bool pipeline_runtime_layer_enabled)76 Result CommandBuffer::SubmitAndReset(uint32_t timeout_ms,
77 bool pipeline_runtime_layer_enabled) {
78 if (device_->GetPtrs()->vkEndCommandBuffer(command_) != VK_SUCCESS)
79 return Result("Vulkan::Calling vkEndCommandBuffer Fail");
80
81 if (device_->GetPtrs()->vkResetFences(device_->GetVkDevice(), 1, &fence_) !=
82 VK_SUCCESS) {
83 return Result("Vulkan::Calling vkResetFences Fail");
84 }
85
86 VkSubmitInfo submit_info = VkSubmitInfo();
87 submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
88 submit_info.commandBufferCount = 1;
89 submit_info.pCommandBuffers = &command_;
90
91 if (device_->GetPtrs()->vkQueueSubmit(device_->GetVkQueue(), 1, &submit_info,
92 fence_) != VK_SUCCESS) {
93 return Result("Vulkan::Calling vkQueueSubmit Fail");
94 }
95
96 guarded_ = false;
97
98 const uint64_t timeout_ns =
99 timeout_ms == static_cast<uint32_t>(~0u) // honor 32bit infinity
100 ? ~0ull
101 : static_cast<uint64_t>(timeout_ms) * 1000ULL * 1000ULL;
102 VkResult r = device_->GetPtrs()->vkWaitForFences(
103 device_->GetVkDevice(), 1, &fence_, VK_TRUE, timeout_ns);
104 if (r == VK_TIMEOUT)
105 return Result("Vulkan::Calling vkWaitForFences Timeout");
106 if (r != VK_SUCCESS) {
107 std::string result_str;
108 switch (r) {
109 case VK_ERROR_OUT_OF_HOST_MEMORY:
110 result_str = "OUT_OF_HOST_MEMORY";
111 break;
112 case VK_ERROR_OUT_OF_DEVICE_MEMORY:
113 result_str = "OUT_OF_DEVICE_MEMORY";
114 break;
115 case VK_ERROR_DEVICE_LOST:
116 result_str = "DEVICE_LOST";
117 break;
118 default:
119 result_str = "<UNEXPECTED RESULT>";
120 break;
121 }
122 return Result("Vulkan::Calling vkWaitForFences Fail (" + result_str + ")");
123 }
124
125 /*
126 google/vulkan-performance-layers requires a call to vkDeviceWaitIdle or
127 vkQueueWaitIdle in order to report the information. Since we want to be
128 able to use that layer in conjunction with Amber we need to somehow
129 communicate that the Amber script has completed.
130 */
131 if (pipeline_runtime_layer_enabled)
132 device_->GetPtrs()->vkQueueWaitIdle(device_->GetVkQueue());
133
134 if (device_->GetPtrs()->vkResetCommandBuffer(command_, 0) != VK_SUCCESS)
135 return Result("Vulkan::Calling vkResetCommandBuffer Fail");
136
137 return {};
138 }
139
Reset()140 void CommandBuffer::Reset() {
141 if (guarded_) {
142 device_->GetPtrs()->vkResetCommandBuffer(command_, 0);
143 guarded_ = false;
144 }
145 }
146
CommandBufferGuard(CommandBuffer * buffer)147 CommandBufferGuard::CommandBufferGuard(CommandBuffer* buffer)
148 : buffer_(buffer) {
149 assert(!buffer_->guarded_);
150 result_ = buffer_->BeginRecording();
151 }
152
~CommandBufferGuard()153 CommandBufferGuard::~CommandBufferGuard() {
154 if (buffer_->guarded_)
155 buffer_->Reset();
156 }
157
Submit(uint32_t timeout_ms,bool pipeline_runtime_layer_enabled)158 Result CommandBufferGuard::Submit(uint32_t timeout_ms,
159 bool pipeline_runtime_layer_enabled) {
160 assert(buffer_->guarded_);
161 return buffer_->SubmitAndReset(timeout_ms, pipeline_runtime_layer_enabled);
162 }
163
164 } // namespace vulkan
165 } // namespace amber
166