UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
PageCache.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5// HEADER_UNIT_SKIP - Not included directly
6
7#include "CoreTypes.h"
10
12{
13 FCriticalSection CriticalSection;
14
15 uint8* LowAddress;
16 uint8* HighAddress;
17 size_t PageSize;
18 uint32 NumPages;
19 uint32 MemSize;
20 uint32 SweepPage;
21 uint32 CommittedPages;
22 uint32 DecommittedPages;
23 uint32 PendingDecommittedPages;
24
25 uint64 CommitHits;
26 uint64 CommitMisses;
27
28
29 FBitTree CurrentlyCommitted;
30 FBitTree NotPendingDecommit;
31
32 uint32 AddrToPageIndex(void* Addr) const
33 {
34 uint32 PageIndex = (((uint8*)Addr) - LowAddress) / PageSize;
35 check(PageIndex < NumPages && (uint8*)Addr >= LowAddress);
36 return PageIndex;
37 }
38
39 void* PageIndexToAddr(uint32 PageIndex) const
40 {
41 check(PageIndex < NumPages);
42 return (void*)(LowAddress + size_t(PageIndex) * PageSize);
43 }
44
45 bool IsCommitted(void* Addr) const
46 {
47 uint32 PageIndex = AddrToPageIndex(Addr);
48 bool bCommitted = CurrentlyCommitted.IsAllocated(PageIndex);
49 check(bCommitted || NotPendingDecommit.IsAllocated(PageIndex)); // can't be decommited and pending decommit
50 return bCommitted;
51 }
52
53 bool IsPendingDecommit(void* Addr) const
54 {
55 uint32 PageIndex = AddrToPageIndex(Addr);
56 bool bPendingDecommit = !NotPendingDecommit.IsAllocated(PageIndex);
57 check(!bPendingDecommit || CurrentlyCommitted.IsAllocated(PageIndex)); // can't be decommited and pending decommit
58 return bPendingDecommit;
59 }
60
61 void Commit(void* Addr)
62 {
63 uint32 PageIndex = AddrToPageIndex(Addr);
64 CurrentlyCommitted.AllocBit(PageIndex);
65 }
66 void Decommit(void* Addr)
67 {
68 uint32 PageIndex = AddrToPageIndex(Addr);
69 CurrentlyCommitted.FreeBit(PageIndex);
70 }
71 void MarkPendingDecommit(void* Addr)
72 {
73 uint32 PageIndex = AddrToPageIndex(Addr);
74 check(IsCommitted(Addr));
75 NotPendingDecommit.FreeBit(PageIndex);
76 }
77 void UnMarkPendingDecommit(void* Addr)
78 {
79 uint32 PageIndex = AddrToPageIndex(Addr);
80 check(IsCommitted(Addr));
81 NotPendingDecommit.AllocBit(PageIndex);
82 }
83
84public:
85
87 : LowAddress((uint8*)InLowAdress)
88 , HighAddress((uint8*)InHighAddress)
89 , PageSize(InPageSize)
90 , NumPages(0)
91 , MemSize(0)
92 , SweepPage(0)
93 , CommittedPages(0)
94 , PendingDecommittedPages(0)
95 {
96 }
97
99 {
100 check(PageSize && LowAddress && HighAddress > LowAddress);
101 NumPages = (HighAddress - LowAddress) / PageSize;
102 MemSize = FBitTree::GetMemoryRequirements(NumPages);
103 return MemSize * 2;
104 }
105
107 {
108 check(NumPages && MemSize);
109 FScopeLock Lock(&CriticalSection);
110 DecommittedPages = NumPages;
111 CurrentlyCommitted.FBitTreeInit(NumPages, Memory, MemSize, false);
112 NotPendingDecommit.FBitTreeInit(NumPages, (uint8*)Memory + MemSize, MemSize, true);
113 }
114
115 size_t MarkForPendingDecommit(void *InAddr, size_t Size)
116 {
117 check(Size > 0 && IsAligned(Size, PageSize));
118 uint32 StartPage = AddrToPageIndex(InAddr);
119 uint32 EndPage = StartPage + Size / PageSize;
120
121 uint32 NumFound = 0;
122
123 FScopeLock Lock(&CriticalSection);
124 // this loop could be accelerated by using the hierarchical info in the bit tree
126 {
127 if (CurrentlyCommitted.IsAllocated(Index) && NotPendingDecommit.IsAllocated(Index))
128 {
129 NumFound++;
130 PendingDecommittedPages++;
131 NotPendingDecommit.FreeBit(Index);
132 }
133 }
134 return size_t(NumFound) * PageSize;
135 }
136
137 template<typename T>
138 size_t Commit(void *InAddr, size_t Size, size_t& OutUnPending, T&& CommitFunction)
139 {
140 check(Size > 0 && IsAligned(Size, PageSize));
141 uint32 StartPage = AddrToPageIndex(InAddr);
142 uint32 EndPage = StartPage + Size / PageSize;
143
146 FScopeLock Lock(&CriticalSection);
147 // this loop could be accelerated by using the hierarchical info in the bit tree
148 uint32 NumFound = 0;
151 {
152 if (CurrentlyCommitted.IsAllocated(Index))
153 {
154 if (!NotPendingDecommit.IsAllocated(Index))
155 {
156 check(PendingDecommittedPages);
157 PendingDecommittedPages--;
158 NumUnPending++;
159 NotPendingDecommit.AllocBit(Index);
160 }
161 }
162 else
163 {
164 NumFound++;
165 CommittedPages++;
166 check(DecommittedPages);
167 DecommittedPages--;
168 check(NotPendingDecommit.IsAllocated(Index));
169 CurrentlyCommitted.AllocBit(Index);
171 {
173 {
174 CommitFunction(PageIndexToAddr(StartCommitPage), (1 + LastCommitPage - StartCommitPage) * PageSize);
175 }
177 }
179 }
180 }
182 {
183 CommitFunction(PageIndexToAddr(StartCommitPage), (1 + LastCommitPage - StartCommitPage) * PageSize);
184 }
185 CommitHits += NumUnPending;
186 CommitMisses += NumFound;
187 OutUnPending = size_t(NumUnPending) * PageSize;
188 return size_t(NumFound) * PageSize;
189 }
190 template<typename T>
191 size_t ForceDecommit(void *InAddr, size_t Size, size_t& OutUnPending, T&& DecommitFunction)
192 {
193 check(Size > 0 && IsAligned(Size, PageSize));
194 uint32 StartPage = AddrToPageIndex(InAddr);
195 uint32 EndPage = StartPage + Size / PageSize;
196
199 FScopeLock Lock(&CriticalSection);
200 // this loop could be accelerated by using the hierarchical info in the bit tree
201 uint32 NumFound = 0;
204 {
205 if (CurrentlyCommitted.IsAllocated(Index))
206 {
207 if (!NotPendingDecommit.IsAllocated(Index))
208 {
209 check(PendingDecommittedPages);
210 PendingDecommittedPages--;
211 NotPendingDecommit.AllocBit(Index);
212 NumUnPending++;
213 }
214 NumFound++;
215 check(CommittedPages);
216 CommittedPages--;
217 DecommittedPages++;
218 CurrentlyCommitted.FreeBit(Index);
220 {
222 {
223 DecommitFunction(PageIndexToAddr(StartCommitPage), (1 + LastCommitPage - StartCommitPage) * PageSize);
224 }
226 }
228 }
229 }
231 {
232 DecommitFunction(PageIndexToAddr(StartCommitPage), (1 + LastCommitPage - StartCommitPage) * PageSize);
233 }
234 OutUnPending = size_t(NumUnPending) * PageSize;
235 return size_t(NumFound) * PageSize;
236 }
237 template<typename T>
239 {
240 if (!NumPages)
241 {
242 return 0; // this page cache was never really set up, nothing to sweep
243 }
244 check(Size > 0 && IsAligned(Size, PageSize));
245 uint32 NumNeed = Size / PageSize;
246
249 uint32 NumFound = 0;
250 FScopeLock Lock(&CriticalSection);
251
252 while (NumFound < NumNeed)
253 {
254 check(SweepPage < NumPages);
255 uint32 Index = NotPendingDecommit.NextAllocBit(SweepPage);
256 if (Index == MAX_uint32)
257 {
258 SweepPage = 0;
259 break;
260 }
261 check(CurrentlyCommitted.IsAllocated(Index) && !NotPendingDecommit.IsAllocated(Index));
262 check(CommittedPages);
263 CommittedPages--;
264 DecommittedPages++;
265 check(PendingDecommittedPages);
266 PendingDecommittedPages--;
267 NumFound++;
268 CurrentlyCommitted.FreeBit(Index);
269 NotPendingDecommit.AllocBit(Index);
271 {
273 {
274 DecommitFunction(PageIndexToAddr(StartDecommitPage), (1 + LastDecommitPage - StartDecommitPage) * PageSize);
275 }
277 }
279 SweepPage = Index + 1;
280 if (SweepPage >= NumPages)
281 {
282 SweepPage = 0;
283 break;
284 }
285 }
287 {
288 DecommitFunction(PageIndexToAddr(StartDecommitPage), (1 + LastDecommitPage - StartDecommitPage) * PageSize);
289 }
290 return size_t(NumFound) * PageSize;
291 }
292 template<typename T>
294 {
295 if (!NumPages)
296 {
297 return 0; // this page cache was never really set up, nothing to sweep
298 }
299 check(Size > 0 && IsAligned(Size, PageSize));
300 uint32 NumNeed = Size / PageSize;
301
304 uint32 NumFound = 0;
305 FScopeLock Lock(&CriticalSection);
306
307 auto DecommitRange = [&]()
308 {
310
311 if (DecommitFunction(PageIndexToAddr(StartDecommitPage), DecommitPageCount * PageSize))
312 {
313 CommittedPages -= DecommitPageCount;
314 DecommittedPages += DecommitPageCount;
315 check(PendingDecommittedPages >= DecommitPageCount);
316 PendingDecommittedPages -= DecommitPageCount;
318
320 for (uint32 BitIndex = StartDecommitPage; BitIndex < EndBitIndex; BitIndex++)
321 {
322 CurrentlyCommitted.FreeBit(BitIndex);
323 NotPendingDecommit.AllocBit(BitIndex);
324 }
325 }
326 };
327 while (NumFound < NumNeed)
328 {
329 check(SweepPage < NumPages);
330 uint32 Index = NotPendingDecommit.NextAllocBit(SweepPage);
331 if (Index == MAX_uint32)
332 {
333 SweepPage = 0;
334 break;
335 }
336 check(CurrentlyCommitted.IsAllocated(Index) && !NotPendingDecommit.IsAllocated(Index));
337 check(CommittedPages);
338
340 {
342 {
344 }
346 }
348 SweepPage = Index + 1;
349 if (SweepPage >= NumPages)
350 {
351 SweepPage = 0;
352 break;
353 }
354 }
356 {
358 }
359 return size_t(NumFound) * PageSize;
360 }
362 {
363 FScopeLock Lock(&CriticalSection);
364 return size_t(PendingDecommittedPages) * PageSize;
365 }
367 {
368 FScopeLock Lock(&CriticalSection);
369 return 100.0f * float(CommitHits) / float(CommitHits + CommitMisses + 1); // +1 to avoid divide by zero
370 }
371};
constexpr bool IsAligned(T Val, uint64 Alignment)
Definition AlignmentTemplates.h:50
#define check(expr)
Definition AssertionMacros.h:314
FPlatformTypes::uint64 uint64
A 64-bit unsigned integer.
Definition Platform.h:1117
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
UE::FPlatformRecursiveMutex FCriticalSection
Definition CriticalSection.h:53
#define MAX_uint32
Definition NumericLimits.h:21
USkinnedMeshComponent float
Definition SkinnedMeshComponent.h:60
FRWLock Lock
Definition UnversionedPropertySerialization.cpp:921
uint32 Size
Definition VulkanMemory.cpp:4034
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition MallocBinnedCommon.h:120
static constexpr uint32 GetMemoryRequirements(uint32 NumPages)
Definition MallocBinnedCommon.h:134
void FBitTreeInit(uint32 InDesiredCapacity, void *Memory, uint32 MemorySize, bool InitialValue)
Definition MallocBinnedCommon.cpp:168
uint32 AllocBit()
Definition MallocBinnedCommon.cpp:229
uint32 NextAllocBit() const
Definition MallocBinnedCommon.cpp:319
void FreeBit(uint32 Index)
Definition MallocBinnedCommon.cpp:415
bool IsAllocated(uint32 Index) const
Definition MallocBinnedCommon.cpp:278
Definition PageCache.h:12
FPageCache(void *InLowAdress, void *InHighAddress, size_t InPageSize)
Definition PageCache.h:86
void InitPageCache(void *Memory)
Definition PageCache.h:106
size_t GetFreeableMemory()
Definition PageCache.h:361
size_t Commit(void *InAddr, size_t Size, size_t &OutUnPending, T &&CommitFunction)
Definition PageCache.h:138
uint32 GetMemoryRequirements()
Definition PageCache.h:98
size_t TryDecommitPending(size_t Size, T &&DecommitFunction)
Definition PageCache.h:293
size_t MarkForPendingDecommit(void *InAddr, size_t Size)
Definition PageCache.h:115
size_t ForceDecommit(void *InAddr, size_t Size, size_t &OutUnPending, T &&DecommitFunction)
Definition PageCache.h:191
size_t DecommitPending(size_t Size, T &&DecommitFunction)
Definition PageCache.h:238
float GetHitRate()
Definition PageCache.h:366
Definition ScopeLock.h:141
U16 Index
Definition radfft.cpp:71