@@ -24,12 +24,12 @@ Note that the paging system is expected to be applicable in different workflows
2424### Key Concepts
2525
26261 . ** Buffer States** : UnKnown, System (Scene), Hardware (Renderer), Disk
27- 2 . ** Buffer Usage** : Static, Dynamic
27+ 2 . ** Buffer Usage** : Static (paged if possible) , Dynamic (paged if necessary)
28283 . ** Core Operations** : Buffer Creation, Data Copy, Buffer Disposal
29294 . ** Paging Operations** :
3030 1 . Page: Create new buffer and fill data. Keep the source buffer (e.g. ` PageToDisk ` ).
3131 2 . Swap: Create new buffer and fill data. Release the source buffer (e.g. ` SwapSceneToDisk ` ).
32- 5 . ** Free Crawling** : Periodic cleanup of resources
32+ 5 . ** Free Crawling** : Periodic cleanup of resources using configurable strategies
3333
3434### Usages
3535
@@ -38,25 +38,26 @@ Ideally, users should use PageBufferManager as the entry point.
3838Usage1: Paging On-demand (asynchronously):
3939``` cpp
4040// Create a Buffer Manager when initializing
41- HdPageableBufferManager <..., ...> bufferManager;
41+ PageableBufferManager <..., ...> bufferManager;
4242
4343// Create a buffer
4444constexpr size_t MB = 1024 * 1024 ;
45- auto buffer1 = bufferManager.CreateBuffer(" AsyncBuffer1 " , 50 * MB);
46-
45+ auto buffer = bufferManager.CreateBuffer(bufferPath , 50 * MB, BufferUsage::Static );
46+
4747// Start some async operations
48- auto swapFuture1 = bufferManager.SwapSystemToDiskAsync(buffer1);
49-
48+ auto swapFuture = bufferManager.SwapSceneToDiskAsync(buffer);
49+ auto pageFuture = bufferManager.PageToSceneMemoryAsync(buffer);
50+
5051// Wait for operations (if needed)
51- swapFuture1 .wait();
52+ swapFuture .wait();
5253// or, wait for all operations to complete
5354// bufferManager.WaitForAllOperations();
5455```
5556
5657Usage2: Moniter & Automatic Freecrawl (recommend in background thread):
5758``` cpp
5859// Create a Buffer Manager when initializing
59- HdPageableBufferManager <..., ...> bufferManager;
60+ PageableBufferManager <..., ...> bufferManager;
6061
6162// Create some buffers with different characteristics using factory
6263constexpr size_t MB = 1024 * 1024 ;
@@ -65,11 +66,16 @@ auto buffer2 = bufferManager.CreateBuffer("MediumBuffer", 50 * MB, BufferUsage::
6566auto buffer3 = bufferManager.CreateBuffer(" LargeBuffer" , 100 * MB, BufferUsage::Static);
6667
6768// ...advance frames when doing the rendering
68- cacheManager .AdvanceFrame();
69+ bufferManager .AdvanceFrame();
6970
7071// In a background thread, at a certain time interval
7172// check 50% of buffers to see if they get chance to page out
72- cacheManager.FreeCrawl(50 .0f );
73+ bufferManager.FreeCrawl(50 .0f );
74+ // or, use async free crawl
75+ // auto futures = bufferManager.FreeCrawlAsync(20.0f);
76+ // for (auto& future : futures) {
77+ // future.wait();
78+ // }
7379```
7480
7581Note that for disposable intermediate data, users should directly control by themselves or using ` std::move ` .
@@ -106,6 +112,7 @@ stateDiagram-v2
106112 EvictHardwareBuffer --> [*]: MakeResident
107113 }
108114
115+ note right of SystemBuffer : Buffer in RAM (in Hydra, it means buffer in the scene side)
109116 note right of HardwareBuffer : Buffer in VRAM (in Hydra, it means buffer has been transferred to Renderer)
110117 note right of HardwareMappedBuffer : Buffer is mapped for GPU access via Graphics API (not used)
111118 note right of EvictHardwareBuffer : Buffer is evicted for saving VRAM via Graphics API (not used)
@@ -115,27 +122,25 @@ The ER diagram shows the key classes and their relationships in the memory pagin
115122
116123``` mermaid
117124erDiagram
118- Buffer {
119- string mName
125+ PageableBuffer {
126+ key mKey
120127 size_t mSize
121128 BufferUsage mUsage
122129 BufferState mBufferState
123- unique_ptr_byte_array mSystemBuffer
124- unique_ptr_byte_array mHardwareBuffer
125- unique_ptr_PageHandle mPageHandle
126- void_ptr mMappedData
127- atomic_int mLockCount
130+ unique_ptr_BufferPageHandle mPageHandle
128131 int mFrameStamp
129- shared_mutex mMapMutex
132+ DestructionCallback mDestructionCallback
130133 }
131134
132- CacheManager {
133- vector_shared_ptr_Buffer mBuffers
134- mutex mBuffersMutex
135- atomic_int mCurrentFrame
135+ PageableBufferManager {
136+ associative_container mBuffers
137+ atomic_uint mCurrentFrame
136138 int mAgeLimit
137- thread mBackgroundThread
138- atomic_bool mStopBackgroundCrawl
139+ PagingStrategyType mPagingStrategy
140+ BufferSelectionStrategyType mBufferSelectionStrategy
141+ unique_ptr_PageFileManager mPageFileManager
142+ unique_ptr_MemoryMonitor mMemoryMonitor
143+ ThreadPool mThreadPool
139144 }
140145
141146 MemoryMonitor {
@@ -148,13 +153,14 @@ erDiagram
148153 float CPU_PAGING_THRESHOLD
149154 }
150155
151- PageManager {
156+ PageFileManager {
152157 vector_unique_ptr_PageFileEntry mPageFileEntries
153158 mutex mSyncMutex
159+ filesystem_path mPageFileDirectory
154160 size_t MAX_PAGE_FILE_SIZE
155161 }
156162
157- PageHandle {
163+ BufferPageHandle {
158164 size_t mPageId
159165 size_t mSize
160166 ptrdiff_t mOffset
@@ -175,65 +181,70 @@ erDiagram
175181 size_t size
176182 }
177183
178- Buffer ||--o| PageHandle : "has"
179- Buffer }o--|| PageManager : "uses for disk operations"
180- Buffer }o--|| MemoryMonitor : "tracks memory usage"
181184
182- CacheManager ||--o{ Buffer : "manages collection of"
183- CacheManager }o--|| MemoryMonitor : "monitors pressure"
185+ PageableBuffer ||--o| BufferPageHandle : "has"
186+ PageableBuffer }o--|| PageFileManager : "uses for disk operations"
187+ PageableBuffer }o--|| MemoryMonitor : "tracks memory usage"
184188
185- PageManager ||--o{ PageFileEntry : "contains"
186- PageManager ||--o{ PageHandle : "creates"
189+ PageableBufferManager ||--o{ PageableBuffer : "manages collection of"
190+ PageableBufferManager }o--|| MemoryMonitor : "monitors pressure"
191+ PageableBufferManager }o--|| PageFileManager : "manages disk storage"
192+
193+ PageFileManager ||--o{ PageFileEntry : "contains"
194+ PageFileManager ||--o{ BufferPageHandle : "creates"
187195
188196 PageFileEntry ||--o{ FreeListEntry : "contains free list"
189197```
190198
191199Core Classes:
192200
193- 1 . ** Buffer ** - The central entity that manages memory buffers across different storage locations (scene/RAM, renderer/VRAM, disk storage)
194- 2 . ** CacheManager ** - Manages the lifecycle and aging of multiple buffers with background processing
195- 3 . ** MemoryMonitor** - Tracks memory usage and calculates memory pressure for both system and hardware memory
196- 4 . ** PageManager ** - Handles disk paging operations and file management
201+ 1 . ** PageableBuffer ** - The central entity that manages memory buffers across different storage locations (scene/RAM, renderer/VRAM, disk storage).
202+ 2 . ** PageableBufferManager ** - Template-based buffer manager with configurable paging and selection strategies. Manages the lifecycle and aging of multiple buffers with background processing.
203+ 3 . ** MemoryMonitor** - Tracks memory usage and calculates memory pressure for both system/scene and hardware/renderer memory.
204+ 4 . ** PageFileManager ** - Handles disk paging operations and file management.
197205
198206Supporting Classes:
199207
200- 1 . ** PageHandle ** - Value object containing page metadata (ID, size, offset) for disk operations
208+ 1 . ** BufferPageHandle ** - Value object containing page metadata (ID, size, offset) for disk operations
2012092 . ** PageFileEntry** - Manages individual page files on disk with free space tracking
2022103 . ** FreeListEntry** - Simple data structure for tracking available free space in page files
203211
212+ Hydra Integration Classes:
213+
214+ 1 . ** HdPageableContainerDataSource** - Memory-managed container data source
215+ 2 . ** HdPageableVectorDataSource** - Memory-managed vector data source
216+ 3 . ** HdPageableSampledDataSource** - Memory-managed sampled data source for time-sampled values
217+ 4 . ** HdPageableBlockDataSource** - Memory-managed block data source
218+
204219Design Patterns:
205220- ** RAII** : Buffer uses RAII for automatic memory management
206221- ** Three-tier Architecture** : System RAM → Hardware/GPU Memory → Disk storage hierarchy
207222- ** Free List Management** : Efficient disk space reuse through gap tracking
208223
209224#### Paging Control
210225
211- Abstract the strategy and decouple it from buffer management. Make the 1) paging, and 2) buffer selection strategies configurable. For safety and simplicity, they should be determined at the compiling time:
226+ The system abstracts paging strategy and buffer selection strategy, decoupling them from buffer management. Both strategies are configurable but should be determined at compile time for simplicity and optimal performance :
212227
213- Configurable Strategies
228+ Configurable Strategies:
214229``` cpp
215230// Concept for paging strategies
216231// Use traits alternatively if C++20 is not supported
217232template <typename T>
218- concept PagingStrategyLike = requires (T t, const OGSDemo::Buffer& buffer, const OGSDemo::PagingContext& context) {
219- { t(buffer, context) } -> std::convertible_to<OGSDemo::PagingDecision>;
220- } || requires (T t, const OGSDemo::Buffer& buffer, const OGSDemo::PagingContext& context) {
221- { t.operator()(buffer, context) } -> std::convertible_to<OGSDemo::PagingDecision>;
233+ concept PagingStrategyLike = requires (T t, const PageableBufferBase& buffer, const PagingContext& context) {
234+ { t(buffer, context) } -> std::convertible_to<PagingDecision>;
222235};
223236
224237// Concept for buffer selection strategies
225- // Use traits alternatively if C++20 is not supported
226- template <typename T>
227- concept BufferSelectionStrategyLike = requires (T t, const std::vector<std::shared_ptr<OGSDemo::Buffer>>& buffers, const OGSDemo::SelectionContext& context) {
228- { t(buffers, context) } -> std::convertible_to<std::vector<std::shared_ptr<OGSDemo::Buffer>>>;
229- } || requires (T t, const std::vector<std::shared_ptr<OGSDemo::Buffer>>& buffers, const OGSDemo::SelectionContext& context) {
230- { t.operator()(buffers, context) } -> std::convertible_to<std::vector<std::shared_ptr<OGSDemo::Buffer>>>;
238+ // Use traits alternatively if C++20 is not supported
239+ template <typename T, typename InputIterator>
240+ concept BufferSelectionStrategyLike = requires (T t, InputIterator first, InputIterator last, const SelectionContext& context) {
241+ { t(first, last, context) } -> std::convertible_to<std::vector<std::shared_ptr<PageableBufferBase>>>;
231242};
232-
233- // Encapsulate in CacheManager
234- template <HdPagingConcepts ::PagingStrategyLike PagingStrategyType,
235- HdPagingConcepts ::BufferSelectionStrategyLike BufferSelectionStrategyType>
236- class HdPageableBufferManager {
243+
244+ // Template-based buffer manager
245+ template <PagingConcepts ::PagingStrategyLike PagingStrategyType,
246+ PagingConcepts ::BufferSelectionStrategyLike BufferSelectionStrategyType>
247+ class PageableBufferManager {
237248// ......
238249private:
239250 // Compile-time strategy instances (no runtime changing)
@@ -242,15 +253,31 @@ private:
242253};
243254```
244255
256+ Set configuration options:
257+ ```cpp
258+ // Configure during BufferManager creation
259+ PageableBufferManager::InitializeDesc desc;
260+ desc.numThreads = 4; // Number of worker threads
261+ desc.pageFileDirectory = std::filesystem::temp_directory_path() / "temp_pages"; // Temp page file dest.
262+ desc.ageLimit= 20; // Frame count before resource is considered old.
263+ desc.sceneMemoryLimit = 2ULL * 1024 * 1024 * 1024; // Byte.
264+ desc.rendererMemoryLimit = 1ULL * 1024 * 1024 * 1024; // Byte.
265+
266+ // Configure background cleanup for MemoryManager
267+ memoryManager.SetFreeCrawlInterval(100); // Check every 100ms
268+ memoryManager.SetFreeCrawlPercentage(10.0f); // Check 10% of buffer
269+ ```
270+
245271Simplified implementation details:
2462721 . Use selection strategy to pick buffer candidates
247273 ``` cpp
248- std::vector<std::shared_ptr<HdPageableBufferBase>> selectedBuffers = mBufferSelectionStrategy(mBuffers, selectionContext);
274+ std::vector<std::shared_ptr<PageableBufferBase>> selectedBuffers =
275+ mBufferSelectionStrategy (mBuffers.begin(), mBuffers.end(), selectionContext);
249276 ```
2502772 . For each buffer, execute paging according to paging configs
251278 ``` cpp
252- PagingDecision decision = mPagingStrategy(buffer, context);
253- bool isDisposed = ExecutePagingDecision(buffer, decision);
279+ PagingDecision decision = mPagingStrategy (* buffer, context);
280+ bool isDisposed = ExecutePagingDecision(* buffer, decision);
254281 ```
255282
256283#### Thread Mode
@@ -263,11 +290,11 @@ graph LR
263290 A[" Create Background Thread" ] --> B[" Create Buffers" ]
264291 B --> G[" Release Buffers" ]
265292 G --> C[" Stop & Join<br/>Background Thread" ]
266- F["CacheManager <br/>MemoryMonitor"]
293+ F[" BufferManager <br/>MemoryMonitor" ]
267294 end
268295
269296 subgraph H[" Background Thread" ]
270- D["Monitor Memory<br/>Every 1000ms "] --> E["Auto Cleanup<br/>when needed"]
297+ D[" Monitor Memory<br/>Every FreeCrawlInterval " ] --> E[" Auto Cleanup<br/>when needed" ]
271298 E --> D
272299 end
273300
@@ -290,19 +317,19 @@ graph LR
290317graph TD
291318 subgraph Caller ["Caller"]
292319 end
293- subgraph CacheManager ["CacheManager "]
320+ subgraph BufferManager ["PageableBufferManager "]
294321 AsyncOps["Async Operations<br/>PageToSystemMemoryAsync()<br/>SwapSystemToDiskAsync()<br/>etc."]
295322 ReturnObj["std::future"]
296323 end
297324
298- subgraph ThreadPool ["ThreadPool"]
325+ subgraph ThreadPool ["Internal ThreadPool"]
299326 Queue["Task Queue"]
300327 Worker1["Worker Thread 1"]
301328 Worker2["Worker Thread 2"]
302329 Worker3["Worker Thread ..."]
303330 end
304331
305- subgraph BufferOps ["Buffer Operations"]
332+ subgraph Operations ["Buffer Operations"]
306333 BufferWork["Actual Memory Operations<br/>Page/Swap/etc."]
307334 end
308335
@@ -320,7 +347,7 @@ graph TD
320347 ReturnObj -.->|"Return"| Caller
321348 BufferWork -.-> ReturnObj
322349
323- style CacheManager fill:#e1f5fe
350+ style BufferManager fill:#e1f5fe
324351 style ThreadPool fill:#f3e5f5
325352 style BufferOps fill:#e8f5e8
326353
0 commit comments