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