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