1 //
2 // Copyright 2020 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/imaging/hgiVulkan/commandBuffer.h"
25 #include "pxr/imaging/hgiVulkan/device.h"
26 #include "pxr/imaging/hgiVulkan/diagnostic.h"
27 #include "pxr/imaging/hgiVulkan/vulkan.h"
28 #include "pxr/base/tf/diagnostic.h"
29 #include <string>
30 
31 PXR_NAMESPACE_OPEN_SCOPE
32 
33 
HgiVulkanCommandBuffer(HgiVulkanDevice * device,VkCommandPool pool)34 HgiVulkanCommandBuffer::HgiVulkanCommandBuffer(
35     HgiVulkanDevice* device,
36     VkCommandPool pool)
37     : _device(device)
38     , _vkCommandPool(pool)
39     , _vkCommandBuffer(nullptr)
40     , _vkFence(nullptr)
41     , _vkSemaphore(nullptr)
42     , _isInFlight(false)
43     , _isSubmitted(false)
44     , _inflightId(0)
45 {
46     VkDevice vkDevice = _device->GetVulkanDevice();
47 
48     // Create vulkan command buffer
49     VkCommandBufferAllocateInfo allocInfo =
50         {VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
51     allocInfo.commandBufferCount = 1;
52     allocInfo.commandPool = pool;
53     allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
54 
55     TF_VERIFY(
56         vkAllocateCommandBuffers(
57             vkDevice,
58             &allocInfo,
59             &_vkCommandBuffer) == VK_SUCCESS
60     );
61 
62     // Assign a debug label to command buffer
63     uint64_t cmdBufHandle = (uint64_t)_vkCommandBuffer;
64     std::string handleStr = std::to_string(cmdBufHandle);
65     std::string cmdBufLbl = "HgiVulkan Command Buffer " + handleStr;
66 
67     HgiVulkanSetDebugName(
68         _device,
69         cmdBufHandle,
70         VK_OBJECT_TYPE_COMMAND_BUFFER,
71         cmdBufLbl.c_str());
72 
73     // CPU synchronization fence. So we known when the cmd buffer can be reused.
74     VkFenceCreateInfo fenceInfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
75     fenceInfo.flags = 0; // Unsignaled starting state
76 
77     TF_VERIFY(
78         vkCreateFence(
79             vkDevice,
80             &fenceInfo,
81             HgiVulkanAllocator(),
82             &_vkFence) == VK_SUCCESS
83     );
84 
85     // Create semaphore for GPU-GPU synchronization
86     VkSemaphoreCreateInfo semaCreateInfo =
87         {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
88     TF_VERIFY(
89         vkCreateSemaphore(
90             vkDevice,
91             &semaCreateInfo,
92             HgiVulkanAllocator(),
93             &_vkSemaphore) == VK_SUCCESS
94     );
95 
96     // Assign a debug label to fence.
97     std::string fenceLbl = "HgiVulkan Fence for Command Buffer: " + handleStr;
98     HgiVulkanSetDebugName(
99         _device,
100         (uint64_t)_vkFence,
101         VK_OBJECT_TYPE_FENCE,
102         fenceLbl.c_str());
103 }
104 
~HgiVulkanCommandBuffer()105 HgiVulkanCommandBuffer::~HgiVulkanCommandBuffer()
106 {
107     VkDevice vkDevice = _device->GetVulkanDevice();
108     vkDestroySemaphore(vkDevice, _vkSemaphore, HgiVulkanAllocator());
109     vkDestroyFence(vkDevice, _vkFence, HgiVulkanAllocator());
110     vkFreeCommandBuffers(vkDevice, _vkCommandPool, 1, &_vkCommandBuffer);
111 }
112 
113 void
BeginCommandBuffer(uint8_t inflightId)114 HgiVulkanCommandBuffer::BeginCommandBuffer(uint8_t inflightId)
115 {
116     if (!_isInFlight) {
117 
118         VkCommandBufferBeginInfo beginInfo =
119             {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
120         beginInfo.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
121 
122         TF_VERIFY(
123             vkBeginCommandBuffer(_vkCommandBuffer, &beginInfo) == VK_SUCCESS
124         );
125 
126         _inflightId = inflightId;
127         _isInFlight = true;
128     }
129 }
130 
131 bool
IsInFlight() const132 HgiVulkanCommandBuffer::IsInFlight() const
133 {
134     return _isInFlight;
135 }
136 
137 void
EndCommandBuffer()138 HgiVulkanCommandBuffer::EndCommandBuffer()
139 {
140     if (_isInFlight) {
141         TF_VERIFY(
142             vkEndCommandBuffer(_vkCommandBuffer) == VK_SUCCESS
143         );
144 
145         _isSubmitted = true;
146     }
147 }
148 
149 bool
ResetIfConsumedByGPU()150 HgiVulkanCommandBuffer::ResetIfConsumedByGPU()
151 {
152     // Command buffer is already available (previously reset).
153     // We do not have to test the fence or reset the cmd buffer.
154     if (!_isInFlight) {
155         return false;
156     }
157 
158     // The command buffer is still recording. We should not test its fence until
159     // we have submitted the command buffer to the queue (vulkan requirement).
160     if (!_isSubmitted) {
161         return false;
162     }
163 
164     VkDevice vkDevice = _device->GetVulkanDevice();
165 
166     // Check the fence to see if the GPU has consumed the command buffer.
167     // We cannnot reuse a command buffer until the GPU is finished with it.
168     if (vkGetFenceStatus(vkDevice, _vkFence) == VK_NOT_READY){
169         return false;
170     }
171 
172     // GPU is done with command buffer, execute the custom fns the client wants
173     // to see executed when cmd buf is consumed.
174     RunAndClearCompletedHandlers();
175 
176     // GPU is done with command buffer, reset fence and command buffer.
177     TF_VERIFY(
178         vkResetFences(vkDevice, 1, &_vkFence)  == VK_SUCCESS
179     );
180 
181     // It might be more efficient to reset the cmd pool instead of individual
182     // command buffers. But we may not have a clear 'StartFrame' / 'EndFrame'
183     // sequence in Hydra. If we did, we could reset the command pool(s) during
184     // Beginframe. Instead we choose to reset each command buffer when it has
185     // been consumed by the GPU.
186 
187     VkCommandBufferResetFlags flags = _GetCommandBufferResetFlags();
188     TF_VERIFY(
189         vkResetCommandBuffer(_vkCommandBuffer, flags) == VK_SUCCESS
190     );
191 
192     // Command buffer may now be reused for new recordings / resource creation.
193     _isInFlight = false;
194     _isSubmitted = false;
195     return true;
196 }
197 
198 VkCommandBuffer
GetVulkanCommandBuffer() const199 HgiVulkanCommandBuffer::GetVulkanCommandBuffer() const
200 {
201     return _vkCommandBuffer;
202 }
203 
204 VkCommandPool
GetVulkanCommandPool() const205 HgiVulkanCommandBuffer::GetVulkanCommandPool() const
206 {
207     return _vkCommandPool;
208 }
209 
210 VkFence
GetVulkanFence() const211 HgiVulkanCommandBuffer::GetVulkanFence() const
212 {
213     return _vkFence;
214 }
215 
216 VkSemaphore
GetVulkanSemaphore() const217 HgiVulkanCommandBuffer::GetVulkanSemaphore() const
218 {
219     return _vkSemaphore;
220 }
221 
222 uint8_t
GetInflightId() const223 HgiVulkanCommandBuffer::GetInflightId() const
224 {
225     return _inflightId;
226 }
227 
228 HgiVulkanDevice*
GetDevice() const229 HgiVulkanCommandBuffer::GetDevice() const
230 {
231     return _device;
232 }
233 
234 void
MemoryBarrier(HgiMemoryBarrier barrier)235 HgiVulkanCommandBuffer::MemoryBarrier(HgiMemoryBarrier barrier)
236 {
237     if (!_vkCommandBuffer) {
238         return;
239     }
240 
241     VkMemoryBarrier memoryBarrier = {VK_STRUCTURE_TYPE_MEMORY_BARRIER};
242 
243     // XXX Flush / stall and invalidate all caches (big hammer!).
244     // Ideally we would set more fine-grained barriers, but we
245     // currently do not get enough information from Hgi to
246     // know what src or dst access there is or what images or
247     // buffers might be affected.
248     TF_VERIFY(barrier==HgiMemoryBarrierAll, "Unsupported barrier");
249 
250     // Who might be generating the data we are interested in reading.
251     memoryBarrier.srcAccessMask =
252         VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
253 
254     // Who might be consuming the data that was writen.
255     memoryBarrier.dstAccessMask =
256         VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
257 
258     vkCmdPipelineBarrier(
259         _vkCommandBuffer,
260         VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, // producer (what we wait for)
261         VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, // consumer (what must wait)
262         0,                                  // flags
263         1,                                  // memoryBarrierCount
264         &memoryBarrier,                     // memory barriers
265         0, nullptr,                         // buffer barriers
266         0, nullptr);                        // image barriers
267 }
268 
269 void
AddCompletedHandler(HgiVulkanCompletedHandler const & fn)270 HgiVulkanCommandBuffer::AddCompletedHandler(HgiVulkanCompletedHandler const& fn)
271 {
272     _completedHandlers.push_back(fn);
273 }
274 
275 void
RunAndClearCompletedHandlers()276 HgiVulkanCommandBuffer::RunAndClearCompletedHandlers()
277 {
278     for (HgiVulkanCompletedHandler& fn : _completedHandlers) {
279         fn();
280     }
281     _completedHandlers.clear();
282 }
283 
284 VkCommandBufferResetFlags
_GetCommandBufferResetFlags()285 HgiVulkanCommandBuffer::_GetCommandBufferResetFlags()
286 {
287     // For now we do not use VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT,
288     // assuming similar memory requirements will be needed each frame.
289     // Releasing resources can come at a performance cost.
290     static const VkCommandBufferResetFlags flags = 0;
291     return flags;
292 }
293 
294 PXR_NAMESPACE_CLOSE_SCOPE
295