1 // 2 // Copyright 2017 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // ResourceVk: 7 // Resource lifetime tracking in the Vulkan back-end. 8 // 9 10 #ifndef LIBANGLE_RENDERER_VULKAN_RESOURCEVK_H_ 11 #define LIBANGLE_RENDERER_VULKAN_RESOURCEVK_H_ 12 13 #include "libANGLE/HandleAllocator.h" 14 #include "libANGLE/renderer/vulkan/vk_utils.h" 15 16 #include <queue> 17 18 namespace rx 19 { 20 namespace vk 21 { 22 // We expect almost all reasonable usage case should have at most 4 current contexts now. When 23 // exceeded, it should still work, but storage will grow. 24 static constexpr size_t kMaxFastQueueSerials = 4; 25 // Serials is an array of queue serials, which when paired with the index of the serials in the 26 // array result in QueueSerials. The array may expand if needed. Since it owned by Resource object 27 // which is protected by shared lock, it is safe to reallocate storage if needed. When it passes to 28 // renderer at garbage collection time, we will make a copy. The array size is expected to be small. 29 // But in future if we run into situation that array size is too big, we can change to packed array 30 // of QueueSerials. 31 using Serials = angle::FastVector<Serial, kMaxFastQueueSerials>; 32 33 // Tracks how a resource is used by ANGLE and by a VkQueue. The serial indicates the most recent use 34 // of a resource in the VkQueue. We use the monotonically incrementing serial number to determine if 35 // a resource is currently in use. 36 class ResourceUse final 37 { 38 public: 39 ResourceUse() = default; 40 ~ResourceUse() = default; 41 ResourceUse(const QueueSerial & queueSerial)42 ResourceUse(const QueueSerial &queueSerial) { setQueueSerial(queueSerial); } ResourceUse(const Serials & otherSerials)43 ResourceUse(const Serials &otherSerials) { mSerials = otherSerials; } 44 45 // Copy constructor ResourceUse(const ResourceUse & other)46 ResourceUse(const ResourceUse &other) : mSerials(other.mSerials) {} 47 ResourceUse &operator=(const ResourceUse &other) 48 { 49 mSerials = other.mSerials; 50 return *this; 51 } 52 53 // Move constructor ResourceUse(ResourceUse && other)54 ResourceUse(ResourceUse &&other) : mSerials(other.mSerials) { other.mSerials.clear(); } 55 ResourceUse &operator=(ResourceUse &&other) 56 { 57 mSerials = other.mSerials; 58 other.mSerials.clear(); 59 return *this; 60 } 61 valid()62 bool valid() const { return mSerials.size() > 0; } 63 reset()64 void reset() { mSerials.clear(); } 65 getSerials()66 const Serials &getSerials() const { return mSerials; } 67 setSerial(SerialIndex index,Serial serial)68 void setSerial(SerialIndex index, Serial serial) 69 { 70 ASSERT(index != kInvalidQueueSerialIndex); 71 if (ANGLE_UNLIKELY(mSerials.size() <= index)) 72 { 73 mSerials.resize(index + 1, kZeroSerial); 74 } 75 ASSERT(mSerials[index] <= serial); 76 mSerials[index] = serial; 77 } 78 setQueueSerial(const QueueSerial & queueSerial)79 void setQueueSerial(const QueueSerial &queueSerial) 80 { 81 setSerial(queueSerial.getIndex(), queueSerial.getSerial()); 82 } 83 84 // Returns true if there is at least one serial is greater than 85 bool operator>(const AtomicQueueSerialFixedArray &serials) const 86 { 87 ASSERT(mSerials.size() <= serials.size()); 88 for (SerialIndex i = 0; i < mSerials.size(); ++i) 89 { 90 if (mSerials[i] > serials[i]) 91 { 92 return true; 93 } 94 } 95 return false; 96 } 97 98 // Returns true if it contains a serial that is greater than 99 bool operator>(const QueueSerial &queuSerial) const 100 { 101 return mSerials.size() > queuSerial.getIndex() && 102 mSerials[queuSerial.getIndex()] > queuSerial.getSerial(); 103 } 104 105 // Returns true if all serials are less than or equal 106 bool operator<=(const AtomicQueueSerialFixedArray &serials) const 107 { 108 ASSERT(mSerials.size() <= serials.size()); 109 for (SerialIndex i = 0; i < mSerials.size(); ++i) 110 { 111 if (mSerials[i] > serials[i]) 112 { 113 return false; 114 } 115 } 116 return true; 117 } 118 usedByCommandBuffer(const QueueSerial & commandBufferQueueSerial)119 bool usedByCommandBuffer(const QueueSerial &commandBufferQueueSerial) const 120 { 121 ASSERT(commandBufferQueueSerial.valid()); 122 // Return true if we have the exact queue serial in the array. 123 return mSerials.size() > commandBufferQueueSerial.getIndex() && 124 mSerials[commandBufferQueueSerial.getIndex()] == 125 commandBufferQueueSerial.getSerial(); 126 } 127 128 // Merge other's serials into this object. merge(const ResourceUse & other)129 void merge(const ResourceUse &other) 130 { 131 if (mSerials.size() < other.mSerials.size()) 132 { 133 mSerials.resize(other.mSerials.size(), kZeroSerial); 134 } 135 136 for (SerialIndex i = 0; i < other.mSerials.size(); ++i) 137 { 138 if (mSerials[i] < other.mSerials[i]) 139 { 140 mSerials[i] = other.mSerials[i]; 141 } 142 } 143 } 144 145 private: 146 // The most recent time of use in a VkQueue. 147 Serials mSerials; 148 }; 149 std::ostream &operator<<(std::ostream &os, const ResourceUse &use); 150 151 class SharedGarbage 152 { 153 public: 154 SharedGarbage(); 155 SharedGarbage(SharedGarbage &&other); 156 SharedGarbage(const ResourceUse &use, GarbageList &&garbage); 157 ~SharedGarbage(); 158 SharedGarbage &operator=(SharedGarbage &&rhs); 159 160 bool destroyIfComplete(RendererVk *renderer); 161 bool hasResourceUseSubmitted(RendererVk *renderer) const; 162 163 private: 164 ResourceUse mLifetime; 165 GarbageList mGarbage; 166 }; 167 168 using SharedGarbageList = std::queue<SharedGarbage>; 169 170 // This is a helper class for back-end objects used in Vk command buffers. They keep a record 171 // of their use in ANGLE and VkQueues via ResourceUse. 172 class Resource : angle::NonCopyable 173 { 174 public: ~Resource()175 virtual ~Resource() {} 176 177 // Complete all recorded and in-flight commands involving this resource 178 angle::Result waitForIdle(ContextVk *contextVk, 179 const char *debugMessage, 180 RenderPassClosureReason reason); 181 setSerial(SerialIndex index,Serial serial)182 void setSerial(SerialIndex index, Serial serial) { mUse.setSerial(index, serial); } 183 setQueueSerial(const QueueSerial & queueSerial)184 void setQueueSerial(const QueueSerial &queueSerial) 185 { 186 mUse.setSerial(queueSerial.getIndex(), queueSerial.getSerial()); 187 } 188 mergeResourceUse(const ResourceUse & use)189 void mergeResourceUse(const ResourceUse &use) { mUse.merge(use); } 190 191 // Check if this resource is used by a command buffer. usedByCommandBuffer(const QueueSerial & commandBufferQueueSerial)192 bool usedByCommandBuffer(const QueueSerial &commandBufferQueueSerial) const 193 { 194 return mUse.usedByCommandBuffer(commandBufferQueueSerial); 195 } 196 getResourceUse()197 const ResourceUse &getResourceUse() const { return mUse; } 198 199 protected: Resource()200 Resource() {} Resource(Resource && other)201 Resource(Resource &&other) : Resource() { mUse = std::move(other.mUse); } 202 Resource &operator=(Resource &&rhs) 203 { 204 std::swap(mUse, rhs.mUse); 205 return *this; 206 } 207 208 // Current resource lifetime. 209 ResourceUse mUse; 210 }; 211 212 // Similar to |Resource| above, this tracks object usage. This includes additional granularity to 213 // track whether an object is used for read-only or read/write access. 214 class ReadWriteResource : public Resource 215 { 216 public: ~ReadWriteResource()217 virtual ~ReadWriteResource() override {} 218 219 // Complete all recorded and in-flight commands involving this resource waitForIdle(ContextVk * contextVk,const char * debugMessage,RenderPassClosureReason reason)220 angle::Result waitForIdle(ContextVk *contextVk, 221 const char *debugMessage, 222 RenderPassClosureReason reason) 223 { 224 return Resource::waitForIdle(contextVk, debugMessage, reason); 225 } 226 setWriteQueueSerial(const QueueSerial & writeQueueSerial)227 void setWriteQueueSerial(const QueueSerial &writeQueueSerial) 228 { 229 mUse.setQueueSerial(writeQueueSerial); 230 mWriteUse.setQueueSerial(writeQueueSerial); 231 } 232 233 // Check if this resource is used by a command buffer. usedByCommandBuffer(const QueueSerial & commandBufferQueueSerial)234 bool usedByCommandBuffer(const QueueSerial &commandBufferQueueSerial) const 235 { 236 return mUse.usedByCommandBuffer(commandBufferQueueSerial); 237 } writtenByCommandBuffer(const QueueSerial & commandBufferQueueSerial)238 bool writtenByCommandBuffer(const QueueSerial &commandBufferQueueSerial) const 239 { 240 return mWriteUse.usedByCommandBuffer(commandBufferQueueSerial); 241 } 242 getWriteResourceUse()243 const ResourceUse &getWriteResourceUse() const { return mWriteUse; } 244 245 protected: ReadWriteResource()246 ReadWriteResource() {} ReadWriteResource(ReadWriteResource && other)247 ReadWriteResource(ReadWriteResource &&other) { *this = std::move(other); } 248 ReadWriteResource &operator=(ReadWriteResource &&other) 249 { 250 Resource::operator=(std::move(other)); 251 mWriteUse = std::move(other.mWriteUse); 252 return *this; 253 } 254 255 // Track write use of the object. Only updated for setWriteQueueSerial(). 256 ResourceUse mWriteUse; 257 }; 258 259 // Adds "void release(RendererVk *)" method for collecting garbage. 260 // Enables RendererScoped<> for classes that support DeviceScoped<>. 261 template <class T> 262 class ReleasableResource final : public Resource 263 { 264 public: 265 // Calls collectGarbage() on the object. 266 void release(RendererVk *renderer); 267 get()268 const T &get() const { return mObject; } get()269 T &get() { return mObject; } 270 271 private: 272 T mObject; 273 }; 274 } // namespace vk 275 } // namespace rx 276 277 #endif // LIBANGLE_RENDERER_VULKAN_RESOURCEVK_H_ 278