-
Notifications
You must be signed in to change notification settings - Fork 227
/
Copy pathPageAllocator.h
217 lines (180 loc) · 7.68 KB
/
PageAllocator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//--------------------------------------------------------------------------------------
// PageAllocator.h
//
// An allocator that maps a virtual memory space to physical pages and allocates memory
// at a page-level granularity. This uses a templated type called a 'MappingPolicy'
// which must provide the following interface:
//
// Must contain static members:
// static constexpr int s_policyId; -- Identifier uniquely specifying the policy type.
// static constexpr int s_blockPageCount; -- Number of memory pages within a page block.
// static constexpr int s_blockSizeBytes; -- Number of bytes within a block (shortcut for s_blockPageCount * c_pageSizeBytes.)
//
// Must contain constructor:
// MappingPolicy(size_t maxSizeBytes);
//
// Must implement methods:
// bool CanExpand(int newBlockCount); -- Determines whether the allocator is able to expand to another block.
// void InitBlock(ID3D12Device* device, void* address, int index, PageBlock& block); -- Initializes a new page block.
// void DeinitBlock(ID3D12Device* device, void* address, int index, PageBlock& block); -- Deinitializes a previously initialized page block.
//
// Advanced Technology Group (ATG)
// Copyright (C) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#pragma once
namespace ATG
{
//
// Represents a range of integral values.
//
struct Range
{
public:
bool Precedes(const Range& subsequent) const;
bool Succeeds(const Range& precedent) const;
static bool MergeLeft(Range& left, const Range& right);
static bool MergeRight(const Range& left, Range& right);
static bool Disjoint(const Range& r0, const Range& r1);
static bool Adjacent(const Range& left, const Range& right);
public:
int start;
int count;
};
//
// Represents a reference to a specific range of a page block.
//
struct PageRef
{
void* baseVirtualAddress;
int blockPageCount;
int blockId;
Range range;
};
//
// Manages the page ranges within a constantly allocated & freed block of pages.
//
struct PageBlock
{
public:
// Returns whether the page block has fully allocated its range of pages.
bool IsExhausted() const { return ranges.empty(); }
// Returns whether the page block is available and fully unallocated.
bool IsFresh() const { return ranges.size() == 1 && ranges[0].count == pagePoolCount; }
// Attempts to allocate 'count' pages in a contiguous range.
bool AllocateRange(int count, Range& outRange);
// Frees a page range back to the allocator.
void FreeRange(const Range& range);
// Resets the allocator back to fully unallocated.
void Reset();
public:
int pagePoolCount;
HANDLE pagePoolHandle;
std::vector<Range> ranges;
};
//
// An allocator that maps a virtual memory space to physical pages and allocates memory at a page-level granularity.
//
template <typename MappingPolicy>
class PageAllocator
{
public:
PageAllocator(ID3D12Device* device, void* baseVirtualAddress, size_t maxSizeBytes);
~PageAllocator();
PageAllocator(const PageAllocator&) = delete;
PageAllocator(PageAllocator&&) = default;
PageAllocator& operator=(const PageAllocator&) = delete;
PageAllocator& operator=(PageAllocator&&) = default;
public:
//
// Uninitializes all blocks. Required step before destruction.
//
void Uninitialize();
//
// Attempts to allocate the specified number of pages from the allocator. Expands the allocator if necessary/possible to accomodate the requested page count.
//
// Param pageCount - number of pages to attempt to allocate.
// Param pageRefs - (out) vector on which to append references to newly-allocated pages.
// Param remaining - (out_opt) if supplied, writes the remaining number of pages that weren't able to be satisfied.
//
void Allocate(int count, std::vector<PageRef>& pageRefs, int* remaining = nullptr);
//
// Releases any pages allocated by the page allocator from the supplied page reference array.
//
// Param pageRefs - vector of references to allocated pages.
//
void Release(const std::vector<PageRef>& pageRefs);
//
// Resets blocks to fully unallocated and nullifies all page references.
//
void NextFrame();
//
// Discards mapped page blocks that haven't been used since the previous 'Clean()' call.
// This allows an application to reclaim memory if a potentially memory-expensive operation
// is performed rarely.
//
void Clean();
//
// Determines the page ranges of a list of page references that originated from this allocator.
//
void GetRanges(const std::vector<PageRef>& pageRefs, std::vector<Range>& ranges);
private:
void* GetBlockAddress(int index) const;
bool Expand(int blockCount);
private:
MappingPolicy m_policy;
ID3D12Device* m_device;
void* m_baseVirtualAddress;
int m_highMark = 0;
int m_freePageCount = 0;
int m_blockCount = 0;
std::vector<PageBlock> m_pools;
};
//
// Implementation of the PageAllocator's MappingPolicy that maps its virtual address space to blocks
// of ESRAM. Can only allocate up to 512- 64-KB pages (32 MB)
//
class EsramMappingPolicy
{
public:
static constexpr int s_policyId = 0x0;
static constexpr int s_blockPageCount = c_esramPageCount;
static constexpr int s_blockSizeBytes = s_blockPageCount * c_pageSizeBytes;
public:
EsramMappingPolicy(size_t maxSizeBytes)
: m_enabled(maxSizeBytes > 0)
{ }
public:
bool CanExpand(int newBlockCount) { return m_enabled && newBlockCount == 1; }
void InitBlock(ID3D12Device* device, void* address, int index, PageBlock& block);
void DeinitBlock(ID3D12Device* device, void* address, int index, PageBlock& block);
private:
bool m_enabled;
};
//
// Implementation of the PageAllocator's MappingPolicy template that maps its virtual address space
// to blocks of DRAM. Fully expandable up to the constraints of the minimum of the system available
// physical memory and the supplied virtual address space.
//
class DramMappingPolicy
{
public:
static constexpr int s_policyId = 0x1;
static constexpr int s_blockPageCount = c_dramBlockPageCount;
static constexpr int s_blockSizeBytes = s_blockPageCount * c_pageSizeBytes;
public:
DramMappingPolicy(size_t maxSizeBytes)
: m_maxSizeBytes(maxSizeBytes)
, m_pageCache()
{ }
bool CanExpand(int newBlockCount);
void InitBlock(ID3D12Device* device, void* address, int index, PageBlock& block);
void DeinitBlock(ID3D12Device* device, void* address, int index, PageBlock& block);
private:
size_t m_maxSizeBytes;
std::vector<ULONG_PTR> m_pageCache;
};
// Less verbose shortcut definitions
using PageAllocatorEsram = PageAllocator<EsramMappingPolicy>;
using PageAllocatorDram = PageAllocator<DramMappingPolicy>;
}
#include "PageAllocator.inl"