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 #include "pxr/imaging/hgiVulkan/buffer.h"
24 #include "pxr/imaging/hgiVulkan/commandQueue.h"
25 #include "pxr/imaging/hgiVulkan/computePipeline.h"
26 #include "pxr/imaging/hgiVulkan/device.h"
27 #include "pxr/imaging/hgiVulkan/garbageCollector.h"
28 #include "pxr/imaging/hgiVulkan/graphicsPipeline.h"
29 #include "pxr/imaging/hgiVulkan/hgi.h"
30 #include "pxr/imaging/hgiVulkan/resourceBindings.h"
31 #include "pxr/imaging/hgiVulkan/sampler.h"
32 #include "pxr/imaging/hgiVulkan/shaderFunction.h"
33 #include "pxr/imaging/hgiVulkan/shaderProgram.h"
34 #include "pxr/imaging/hgiVulkan/texture.h"
35
36 #include "pxr/base/arch/hints.h"
37 #include "pxr/base/tf/diagnostic.h"
38
39
40 PXR_NAMESPACE_OPEN_SCOPE
41
42 std::vector<HgiVulkanBufferVector*>
43 HgiVulkanGarbageCollector::_bufferList;
44 std::vector<HgiVulkanTextureVector*>
45 HgiVulkanGarbageCollector::_textureList;
46 std::vector<HgiVulkanSamplerVector*>
47 HgiVulkanGarbageCollector::_samplerList;
48 std::vector<HgiVulkanShaderFunctionVector*>
49 HgiVulkanGarbageCollector::_shaderFunctionList;
50 std::vector<HgiVulkanShaderProgramVector*>
51 HgiVulkanGarbageCollector::_shaderProgramList;
52 std::vector<HgiVulkanResourceBindingsVector*>
53 HgiVulkanGarbageCollector::_resourceBindingsList;
54 std::vector<HgiVulkanGraphicsPipelineVector*>
55 HgiVulkanGarbageCollector::_graphicsPipelineList;
56 std::vector<HgiVulkanComputePipelineVector*>
57 HgiVulkanGarbageCollector::_computePipelineList;
58
59
60 template<class T>
_EmptyTrash(std::vector<std::vector<T * > * > * list,VkDevice vkDevice,uint64_t queueInflightBits)61 static void _EmptyTrash(
62 std::vector<std::vector<T*>*>* list,
63 VkDevice vkDevice,
64 uint64_t queueInflightBits)
65 {
66 // Loop the garbage vectors of each thread
67 for (auto vec : *list) {
68 for (size_t i=vec->size(); i-- > 0;) {
69 T* object = (*vec)[i];
70
71 // Each device has its own queue, so its own set of inflight bits.
72 // We must only destroy objects that belong to this device & queue.
73 // (The garbage collector collects objects from all devices)
74 if (vkDevice != object->GetDevice()->GetVulkanDevice()) {
75 continue;
76 }
77
78 // See comments in PerformGarbageCollection.
79 if ((queueInflightBits & object->GetInflightBits()) == 0) {
80 delete object;
81 std::iter_swap(vec->begin() + i, vec->end() - 1);
82 vec->pop_back();
83 }
84 }
85 }
86 }
87
HgiVulkanGarbageCollector(HgiVulkan * hgi)88 HgiVulkanGarbageCollector::HgiVulkanGarbageCollector(HgiVulkan* hgi)
89 : _hgi(hgi)
90 , _isDestroying(false)
91 {
92 }
93
94 HgiVulkanGarbageCollector::~HgiVulkanGarbageCollector() = default;
95
96 /* Multi threaded */
97 HgiVulkanBufferVector*
GetBufferList()98 HgiVulkanGarbageCollector::GetBufferList()
99 {
100 return _GetThreadLocalStorageList(&_bufferList);
101 }
102
103 /* Multi threaded */
104 HgiVulkanTextureVector*
GetTextureList()105 HgiVulkanGarbageCollector::GetTextureList()
106 {
107 return _GetThreadLocalStorageList(&_textureList);
108 }
109
110 /* Multi threaded */
111 HgiVulkanSamplerVector*
GetSamplerList()112 HgiVulkanGarbageCollector::GetSamplerList()
113 {
114 return _GetThreadLocalStorageList(&_samplerList);
115 }
116
117 /* Multi threaded */
118 HgiVulkanShaderFunctionVector*
GetShaderFunctionList()119 HgiVulkanGarbageCollector::GetShaderFunctionList()
120 {
121 return _GetThreadLocalStorageList(&_shaderFunctionList);
122 }
123
124 /* Multi threaded */
125 HgiVulkanShaderProgramVector*
GetShaderProgramList()126 HgiVulkanGarbageCollector::GetShaderProgramList()
127 {
128 return _GetThreadLocalStorageList(&_shaderProgramList);
129 }
130
131 /* Multi threaded */
132 HgiVulkanResourceBindingsVector*
GetResourceBindingsList()133 HgiVulkanGarbageCollector::GetResourceBindingsList()
134 {
135 return _GetThreadLocalStorageList(&_resourceBindingsList);
136 }
137
138 /* Multi threaded */
139 HgiVulkanGraphicsPipelineVector*
GetGraphicsPipelineList()140 HgiVulkanGarbageCollector::GetGraphicsPipelineList()
141 {
142 return _GetThreadLocalStorageList(&_graphicsPipelineList);
143 }
144
145 /* Multi threaded */
146 HgiVulkanComputePipelineVector*
GetComputePipelineList()147 HgiVulkanGarbageCollector::GetComputePipelineList()
148 {
149 return _GetThreadLocalStorageList(&_computePipelineList);
150 }
151
152 /* Single threaded */
153 void
PerformGarbageCollection(HgiVulkanDevice * device)154 HgiVulkanGarbageCollector::PerformGarbageCollection(HgiVulkanDevice* device)
155 {
156 // Garbage Collection notes:
157 //
158 // When the client requests objects to be destroyed (Eg. Hgi::DestroyBuffer)
159 // we put objects into this garbage collector. At that time we also store
160 // the bits of the command buffers that are 'in-flight'.
161 // We have to delay destroying the vulkan resources until there are no
162 // command buffers using the resource.
163 // Instead of tracking complex dependencies between objects and cmd buffers
164 // we simply assume that all in-flight command buffers might be using the
165 // destroyed object and wait until those command buffers have been
166 // consumed by the GPU.
167 //
168 // In _EmptyTrash we try to delete objects in the garbage collector.
169 // We compare the bits of the queue and the object to decide if we can
170 // delete the object. Example:
171 //
172 // Each command buffer takes up one bit (where 1 means "in-flight").
173 // Queue currently in-flight cmd buf bits: 01001011101
174 // In-flight bits when obj was trashed: 00100000100
175 // Bitwise & result: 00000000100
176 //
177 // Conclusion: object cannot yet be destroyed. One command buffer that was
178 // in-flight during the destruction request is still in-flight and might
179 // still be using the object on the GPU.
180
181 _isDestroying = true;
182
183 // Check what command buffers are in-flight on the device queue.
184 HgiVulkanCommandQueue* queue = device->GetCommandQueue();
185 uint64_t queueBits = queue->GetInflightCommandBuffersBits();
186 VkDevice vkDevice = device->GetVulkanDevice();
187
188 _EmptyTrash(&_bufferList, vkDevice, queueBits);
189 _EmptyTrash(&_textureList, vkDevice, queueBits);
190 _EmptyTrash(&_samplerList, vkDevice, queueBits);
191 _EmptyTrash(&_shaderFunctionList, vkDevice, queueBits);
192 _EmptyTrash(&_shaderProgramList, vkDevice, queueBits);
193 _EmptyTrash(&_resourceBindingsList, vkDevice, queueBits);
194 _EmptyTrash(&_graphicsPipelineList, vkDevice, queueBits);
195 _EmptyTrash(&_computePipelineList, vkDevice, queueBits);
196
197 _isDestroying = false;
198 }
199
200 template<class T>
_GetThreadLocalStorageList(std::vector<T * > * collector)201 T* HgiVulkanGarbageCollector::_GetThreadLocalStorageList(std::vector<T*>* collector)
202 {
203 if (ARCH_UNLIKELY(_isDestroying)) {
204 TF_CODING_ERROR("Cannot destroy object during garbage collection ");
205 while(_isDestroying);
206 }
207
208 // Only lock and create a new garbage vector if we dont have one in TLS.
209 // Using TLS means this we store per type T, not per T and Hgi instance.
210 // So if you call garbage collect on one Hgi, it destroys objects across
211 // all Hgi's. This should be ok since we only call the destructor of the
212 // garbage object.
213 thread_local T* _tls = nullptr;
214 static std::mutex garbageMutex;
215
216 if (!_tls) {
217 _tls = new T();
218 std::lock_guard<std::mutex> guard(garbageMutex);
219 collector->push_back(_tls);
220 }
221 return _tls;
222 }
223
224
225 PXR_NAMESPACE_CLOSE_SCOPE
226