Skip to content

Commit bc10111

Browse files
Merge pull request #1691 from vsg-dev/ScavangeResources
Improved DatabasePager automatic load balancing
2 parents 24f21da + f737a7d commit bc10111

23 files changed

Lines changed: 324 additions & 174 deletions

include/vsg/app/CompileManager.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ namespace vsg
3939
bool requiresViewerUpdate() const;
4040
};
4141

42+
/// ResourceScavenger provides a mechanism for releasing and reusing unused resources when allocation of required GPU memory fails.
43+
class VSG_DECLSPEC ResourceScavenger : public Inherit<Object, ResourceScavenger>
44+
{
45+
public:
46+
explicit ResourceScavenger(ref_ptr<DatabasePager> in_databasePager);
47+
48+
virtual bool scavenge(ResourceRequirements& resourceRequirements);
49+
50+
uint64_t sleepDuration = 16 * 5; /// milliseconds sleep to make after adjusting load targets to allow other threads to free up space, default to 5 frames at 60fps
51+
observer_ptr<DatabasePager> databasePager;
52+
};
53+
4254
/// CompileManager is a helper class that compiles subgraphs for the windows/framebuffers associated with the CompileManager.
4355
class VSG_DECLSPEC CompileManager : public Inherit<Object, CompileManager>
4456
{
@@ -71,7 +83,15 @@ namespace vsg
7183
/// compile all the command graphs in a task
7284
CompileResult compileTask(ref_ptr<RecordAndSubmitTask> task, const ResourceRequirements& resourceRequirements = {});
7385

86+
/// mechanism for releasing and reusing used resources
87+
ref_ptr<ResourceScavenger> resourceScavenger;
88+
89+
std::atomic_uint successfulCompileCount{0};
90+
std::atomic_uint failedCompileCount{0};
91+
7492
protected:
93+
~CompileManager();
94+
7595
using CompileTraversals = ThreadSafeQueue<ref_ptr<CompileTraversal>>;
7696
size_t numCompileTraversals = 0;
7797
ref_ptr<CompileTraversals> compileTraversals;

include/vsg/core/observer_ptr.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ namespace vsg
185185
return {};
186186
}
187187

188+
/// We strongly recommend access via ref_ptr<> rather than get(). Only use get() to get access to the raw C pointer when it's know that the object pointed to will remain in memory through use of that raw C pointer.
189+
T* get() const noexcept { return _ptr; }
190+
188191
protected:
189192
template<class R>
190193
friend class observer_ptr;

include/vsg/core/type_name.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ namespace vsg
4949
template<> constexpr const char* vsg::type_name<T>() noexcept { return #T; } \
5050
template<> constexpr const char* vsg::type_name<const T>() noexcept { return "const "#T; }
5151

52-
/// convinience function for adding a space in front of the type_name string.
52+
/// convenience function for adding a space in front of the type_name string.
5353
template<typename T>
5454
std::string space_type_name(const T&) { return std::string(" ") + type_name<T>(); }
5555

include/vsg/io/DatabasePager.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,10 @@ namespace vsg
104104

105105
void add(ref_ptr<PagedLOD> plod, const CompileResult& cr);
106106

107-
ref_ptr<PagedLOD> take_when_available();
107+
/// prune entries older than specified frameCount
108+
uint32_t prune(uint64_t frameCount);
109+
110+
ref_ptr<PagedLOD> take_when_available(uint64_t frameCount);
108111

109112
Nodes take_all(CompileResult& result);
110113

@@ -138,13 +141,16 @@ namespace vsg
138141
ref_ptr<CompileManager> compileManager;
139142

140143
std::atomic_uint numActiveRequests{0};
141-
std::atomic_uint64_t frameCount;
144+
std::atomic_uint64_t frameCount{0};
142145

143146
ref_ptr<CulledPagedLODs> culledPagedLODs;
144147

145148
/// for systems with smaller GPU memory limits you may need to reduce the targetMaxNumPagedLODWithHighResSubgraphs to keep memory usage within available limits.
146149
uint32_t targetMaxNumPagedLODWithHighResSubgraphs = 1500;
147150

151+
/// number of frames before a PagedLOD with a failed load/compile is attempted to be loaded/compiled again.
152+
uint64_t delayBeforeNextLoadAttempt = 60;
153+
148154
std::mutex pendingPagedLODMutex;
149155

150156
ref_ptr<PagedLODContainer> pagedLODContainer;
@@ -158,16 +164,16 @@ namespace vsg
158164
/// read and delete threads created by start()
159165
std::list<std::thread> threads;
160166

167+
ref_ptr<ActivityStatus> status;
168+
ref_ptr<DeleteQueue> deleteQueue;
169+
161170
protected:
162171
virtual ~DatabasePager();
163172

164173
void requestDiscarded(PagedLOD* plod);
165174

166-
ref_ptr<ActivityStatus> _status;
167-
168175
ref_ptr<DatabaseQueue> _requestQueue;
169176
ref_ptr<DatabaseQueue> _toMergeQueue;
170-
ref_ptr<DeleteQueue> _deleteQueue;
171177
};
172178
VSG_type_name(vsg::DatabasePager);
173179

include/vsg/nodes/InstanceNode.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ namespace vsg
2121
{
2222

2323
/// InstanceNode provides a mechanism for specifying the translations, rotations and scales (transform arrays) of subgraph
24-
/// that contains InstanceDraw leaf node(s) that utlize the InstanceNode's per instance transform arrays combined with the
24+
/// that contains InstanceDraw leaf node(s) that utilize the InstanceNode's per instance transform arrays combined with the
2525
/// InstanceDraw nodes per vertex arrays.
2626
///
27-
/// InstanceNode only work correctly when the child subgraphs that obey these contraints:
27+
/// InstanceNode only work correctly when the child subgraphs that obey these constraints:
2828
/// 1. Do not contain any Transform nodes
2929
/// 2. Do not contain any Culling nodes
3030
/// 3. Do not contain any InstanceNode nodes

include/vsg/nodes/PagedLOD.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ namespace vsg
8989
mutable std::atomic_uint64_t frameHighResLastUsed{0};
9090
mutable std::atomic_uint requestCount{0};
9191

92+
mutable std::atomic_uint64_t frameNextLoadAttempt{0};
93+
mutable std::atomic_uint64_t loadAttempts{0};
94+
9295
enum RequestStatus : unsigned int
9396
{
9497
NoRequest = 0,

include/vsg/threading/DeleteQueue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ namespace vsg
3737

3838
std::atomic_uint64_t frameCount = 0;
3939
uint64_t retainForFrameCount = 3;
40+
std::atomic_uint64_t deletedCount = 0;
4041

4142
ActivityStatus* getStatus() { return _status; }
4243
const ActivityStatus* getStatus() const { return _status; }

include/vsg/vk/Device.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ namespace vsg
8585
/// return true if Device was created with specified extension
8686
bool supportsDeviceExtension(const char* extensionName) const;
8787

88-
/// return the amount of memory available in deviceMemoryBufferPools and allocatable on device
89-
VkDeviceSize availableMemory(bool includeMemoryPools = true) const;
88+
/// return the amount of remaining memory, compatible with specified flags, available that can be allocated.
89+
VkDeviceSize availableMemory(VkMemoryPropertyFlags memoryPropertiesFlags, double allocatedMemoryLimit = 1.0) const;
9090

9191
// provide observer_ptr to memory buffer, descriptor pools and transferTask so that these can be accessed when required
9292
observer_ptr<MemoryBufferPools> deviceMemoryBufferPools;

include/vsg/vk/DeviceMemory.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ namespace vsg
4444
const VkMemoryPropertyFlags& getMemoryPropertyFlags() const { return _properties; }
4545

4646
MemorySlots::OptionalOffset reserve(VkDeviceSize size);
47+
MemorySlots::OptionalOffset reserve(VkDeviceSize size, VkDeviceSize alignment);
4748
void release(VkDeviceSize offset, VkDeviceSize size);
4849

4950
bool full() const;

src/vsg/app/CompileManager.cpp

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
2121

2222
using namespace vsg;
2323

24+
////////////////////////////////////////////////////////////////////////////////////////////////////
25+
//
26+
// CompileResult
27+
//
2428
void CompileResult::reset()
2529
{
2630
result = VK_INCOMPLETE;
@@ -66,6 +70,51 @@ bool CompileResult::requiresViewerUpdate() const
6670
return false;
6771
}
6872

73+
////////////////////////////////////////////////////////////////////////////////////////////////////
74+
//
75+
// CompileManager
76+
//
77+
78+
ResourceScavenger::ResourceScavenger(ref_ptr<DatabasePager> in_databasePager) :
79+
databasePager(in_databasePager)
80+
{
81+
}
82+
83+
bool ResourceScavenger::scavenge(ResourceRequirements& /*resourceRequirements*/)
84+
{
85+
bool scavenged = false;
86+
87+
// get raw C pointer to avoid a database pager thread invoking scavenger and keeping the database pager alive and pausing destruction
88+
if (auto ref_databasePager = databasePager.get())
89+
{
90+
if (!ref_databasePager->status->active()) return false;
91+
92+
uint32_t targetPagedLOD = ref_databasePager->pagedLODContainer->activeList.count;
93+
if (ref_databasePager->pagedLODContainer->inactiveList.count > ref_databasePager->numActiveRequests) targetPagedLOD += ref_databasePager->pagedLODContainer->inactiveList.count - ref_databasePager->numActiveRequests;
94+
95+
if (targetPagedLOD < ref_databasePager->targetMaxNumPagedLODWithHighResSubgraphs)
96+
{
97+
debug("ResourceScavenger::scavenge(..) resetting databasePager->targetMaxNumPagedLODWithHighResSubgraphs to ", targetPagedLOD);
98+
99+
ref_databasePager->targetMaxNumPagedLODWithHighResSubgraphs = targetPagedLOD;
100+
}
101+
102+
auto before_deletedCount = ref_databasePager->deleteQueue->deletedCount.load();
103+
104+
if (sleepDuration > 0) std::this_thread::sleep_for(std::chrono::milliseconds(sleepDuration));
105+
106+
auto after_deletedCount = ref_databasePager->deleteQueue->deletedCount.load();
107+
108+
scavenged = (after_deletedCount > before_deletedCount);
109+
}
110+
111+
return scavenged;
112+
}
113+
114+
////////////////////////////////////////////////////////////////////////////////////////////////////
115+
//
116+
// CompileManager
117+
//
69118
CompileManager::CompileManager(Viewer& viewer, ref_ptr<ResourceHints> hints)
70119
{
71120
compileTraversals = CompileTraversals::create(viewer.status);
@@ -84,6 +133,11 @@ CompileManager::CompileManager(Viewer& viewer, ref_ptr<ResourceHints> hints)
84133
#endif
85134
}
86135

136+
CompileManager::~CompileManager()
137+
{
138+
vsg::info("CompileManager::~CompileManager() successfulCompileCount= ", successfulCompileCount, ", failedCompileCount = ", failedCompileCount);
139+
}
140+
87141
CompileManager::CompileTraversals::container_type CompileManager::takeCompileTraversals(size_t count)
88142
{
89143
CompileTraversals::container_type cts;
@@ -167,6 +221,7 @@ CompileResult CompileManager::compile(ref_ptr<Object> object, ContextSelectionFu
167221
auto& requirements = collectRequirements.requirements;
168222
auto& viewDetailsStack = requirements.viewDetailsStack;
169223

224+
VkResult reserve_result = VK_INCOMPLETE;
170225
CompileResult result;
171226
result.maxSlots = requirements.maxSlots;
172227
result.containsPagedLOD = requirements.containsPagedLOD;
@@ -199,9 +254,24 @@ CompileResult CompileManager::compile(ref_ptr<Object> object, ContextSelectionFu
199254
}
200255
}
201256
}
257+
}
202258

203-
auto reserveResult = context->reserve(requirements);
204-
if (reserveResult != VK_SUCCESS) throw vsg::Exception{"Context::reserve() failed", reserveResult};
259+
for (auto& context : compileTraversal->contexts)
260+
{
261+
reserve_result = context->reserve(requirements);
262+
263+
// vsg::info(" done reserve context->reserve() ", reserve_result);
264+
if (reserve_result != VK_SUCCESS && resourceScavenger && resourceScavenger->scavenge(requirements))
265+
{
266+
reserve_result = context->reserve(requirements);
267+
}
268+
269+
if (reserve_result != VK_SUCCESS)
270+
{
271+
result.message = vsg::make_string("Context::reserve() failed", reserve_result);
272+
result.result = reserve_result;
273+
return;
274+
}
205275
}
206276

207277
object->accept(*compileTraversal);
@@ -251,6 +321,15 @@ CompileResult CompileManager::compile(ref_ptr<Object> object, ContextSelectionFu
251321

252322
compileTraversals->add(compileTraversal);
253323

324+
if (result.result == VK_SUCCESS)
325+
{
326+
++successfulCompileCount;
327+
}
328+
else
329+
{
330+
++failedCompileCount;
331+
}
332+
254333
return result;
255334
}
256335

0 commit comments

Comments
 (0)