@@ -9,51 +9,30 @@ uint64_t GpuMemoryAllocator::allocate_buffer(size_t byte_size, BufferType type,
99
1010 auto descriptor = BufferDescriptor{type, byte_size, MemoryProperty::HostVisibleAndCoherent};
1111
12- auto now = std::chrono::steady_clock::now ();
13-
14- // Try to find a free object.
15- for (int free_object_index = 0 ; free_object_index < free_objects.size (); free_object_index++) {
16- auto free_obj = free_objects[free_object_index];
17-
18- bool found_free_obj = false ;
12+ // Try to find a free object in the idle pool.
13+ // Anything in idle_pool is confirmed safe by frame-delay.
14+ for (int i = 0 ; i < (int )idle_pool.size (); i++) {
15+ auto & free_obj = idle_pool[i];
1916
2017 if (free_obj.kind == FreeObjectKind::Buffer && free_obj.buffer_allocation .descriptor == descriptor) {
21- std::chrono::duration<double > duration = now - free_obj.timestamp ;
22-
23- // Check if this free buffer can be reused.
24- if (duration.count () >= REUSE_TIME ) {
25- found_free_obj = true ;
26- }
27- }
28-
29- if (!found_free_obj) {
30- continue ;
31- }
18+ uint64_t id = free_obj.id ;
19+ BufferAllocation allocation = free_obj.buffer_allocation ;
3220
33- // Reuse this free object.
34- free_objects.erase (free_objects.begin () + free_object_index);
21+ idle_pool.erase (idle_pool.begin () + i);
3522
36- uint64_t id = free_obj. id ;
37- BufferAllocation allocation = free_obj. buffer_allocation ;
23+ allocation. tag = tag ;
24+ allocation. buffer -> set_label (tag) ;
3825
39- // Update tag. Also update GPU debug marker.
40- allocation.tag = tag;
41- allocation.buffer ->set_label (tag);
26+ bytes_committed += byte_size;
27+ active_buffers[id] = allocation;
4228
43- bytes_committed += byte_size;
44- buffers_in_use[id] = allocation;
45-
46- return id;
29+ return id;
30+ }
4731 }
4832
49- // Create a new buffer.
50-
5133 auto buffer = device->create_buffer (descriptor, tag);
52-
53- auto id = next_buffer_id;
54- next_buffer_id += 1 ;
55-
56- buffers_in_use[id] = BufferAllocation{buffer, descriptor, tag};
34+ auto id = next_buffer_id++;
35+ active_buffers[id] = BufferAllocation{buffer, descriptor, tag};
5736
5837 bytes_allocated += byte_size;
5938 bytes_committed += byte_size;
@@ -65,40 +44,31 @@ uint64_t GpuMemoryAllocator::allocate_texture(Vec2I size, TextureFormat format,
6544 assert (!size.is_any_zero ());
6645
6746 auto descriptor = TextureDescriptor{size, format};
68-
6947 auto byte_size = descriptor.byte_size ();
7048
71- // Try to find a free object.
72- for (int free_object_index = 0 ; free_object_index < free_objects.size (); free_object_index++) {
73- auto free_obj = free_objects[free_object_index];
74-
75- if (!(free_obj.kind == FreeObjectKind::Texture && free_obj.texture_allocation .descriptor == descriptor)) {
76- continue ;
77- }
49+ // Try to find a free object in the idle pool.
50+ for (int i = 0 ; i < (int )idle_pool.size (); i++) {
51+ auto & free_obj = idle_pool[i];
7852
79- // Reuse this free object.
80- free_objects.erase (free_objects.begin () + free_object_index);
53+ if (free_obj.kind == FreeObjectKind::Texture && free_obj.texture_allocation .descriptor == descriptor) {
54+ uint64_t id = free_obj.id ;
55+ auto allocation = free_obj.texture_allocation ;
8156
82- uint64_t id = free_obj.id ;
83- auto allocation = free_obj.texture_allocation ;
57+ idle_pool.erase (idle_pool.begin () + i);
8458
85- // Update tag. Also update GPU debug marker.
86- allocation.tag = tag;
87- allocation.texture ->set_label (tag);
59+ allocation.tag = tag;
60+ allocation.texture ->set_label (tag);
8861
89- bytes_committed += byte_size;
90- textures_in_use [id] = allocation;
62+ bytes_committed += byte_size;
63+ active_textures [id] = allocation;
9164
92- return id;
65+ return id;
66+ }
9367 }
9468
95- // Create a new texture.
96-
9769 auto texture = device->create_texture (descriptor, tag);
98- auto id = next_texture_id;
99- next_texture_id += 1 ;
100-
101- textures_in_use[id] = TextureAllocation{texture, descriptor, tag};
70+ auto id = next_texture_id++;
71+ active_textures[id] = TextureAllocation{texture, descriptor, tag};
10272
10373 bytes_allocated += byte_size;
10474 bytes_committed += byte_size;
@@ -107,131 +77,124 @@ uint64_t GpuMemoryAllocator::allocate_texture(Vec2I size, TextureFormat format,
10777}
10878
10979void GpuMemoryAllocator::free_buffer (uint64_t id) {
110- if (buffers_in_use.find (id) == buffers_in_use.end ()) {
80+ auto it = active_buffers.find (id);
81+ if (it == active_buffers.end ()) {
11182 Logger::error (" Attempted to free unallocated buffer!" );
11283 return ;
11384 }
11485
115- auto allocation = buffers_in_use[id];
116-
117- buffers_in_use.erase (id);
86+ auto allocation = it->second ;
87+ active_buffers.erase (it);
11888
11989 bytes_committed -= allocation.buffer ->get_size ();
12090
121- // Put the buffer back to the free objects.
122-
12391 FreeObject free_obj;
12492 free_obj.timestamp = std::chrono::steady_clock::now ();
12593 free_obj.kind = FreeObjectKind::Buffer;
12694 free_obj.id = id;
12795 free_obj.buffer_allocation = allocation;
12896
129- free_objects.push_back (free_obj);
97+ // Put into current frame bucket.
98+ pending_buckets[current_frame_index % MAX_FRAMES_IN_FLIGHT ].objects .push_back (free_obj);
13099}
131100
132101void GpuMemoryAllocator::free_texture (uint64_t id) {
133- if (textures_in_use.find (id) == textures_in_use.end ()) {
102+ auto it = active_textures.find (id);
103+ if (it == active_textures.end ()) {
134104 Logger::error (" Attempted to free unallocated texture!" );
135105 return ;
136106 }
137107
138- auto allocation = textures_in_use[id];
139-
140- textures_in_use.erase (id);
108+ auto allocation = it->second ;
109+ active_textures.erase (it);
141110
142- auto byte_size = allocation.descriptor .byte_size ();
143- bytes_committed -= byte_size;
111+ bytes_committed -= allocation.descriptor .byte_size ();
144112
145113 FreeObject free_obj;
146114 free_obj.timestamp = std::chrono::steady_clock::now ();
147115 free_obj.kind = FreeObjectKind::Texture;
148116 free_obj.id = id;
149117 free_obj.texture_allocation = allocation;
150118
151- free_objects.push_back (free_obj);
119+ // Put into current frame bucket.
120+ pending_buckets[current_frame_index % MAX_FRAMES_IN_FLIGHT ].objects .push_back (free_obj);
152121}
153122
154123std::shared_ptr<Buffer> GpuMemoryAllocator::get_buffer (uint64_t id) {
155- if (buffers_in_use.find (id) == buffers_in_use.end ()) {
124+ auto it = active_buffers.find (id);
125+ if (it == active_buffers.end ()) {
156126 Logger::error (" Attempted to get unallocated buffer!" );
157127 return nullptr ;
158128 }
159-
160- return buffers_in_use[id].buffer ;
129+ return it->second .buffer ;
161130}
162131
163132std::shared_ptr<Texture> GpuMemoryAllocator::get_texture (uint64_t id) {
164- if (textures_in_use.find (id) == textures_in_use.end ()) {
133+ auto it = active_textures.find (id);
134+ if (it == active_textures.end ()) {
165135 Logger::error (" Attempted to get unallocated texture!" );
166136 return nullptr ;
167137 }
138+ return it->second .texture ;
139+ }
140+
141+ void GpuMemoryAllocator::begin_frame () {
142+ // Move to next frame.
143+ current_frame_index++;
168144
169- return textures_in_use[id].texture ;
145+ // Reclaim the bucket we are about to overwrite.
146+ // This bucket contains objects that have been "cooling down" for MAX_FRAMES_IN_FLIGHT frames.
147+ auto & bucket = pending_buckets[current_frame_index % MAX_FRAMES_IN_FLIGHT ];
148+ if (!bucket.objects .empty ()) {
149+ idle_pool.insert (idle_pool.end (), bucket.objects .begin (), bucket.objects .end ());
150+ bucket.objects .clear ();
151+ }
170152}
171153
172154void GpuMemoryAllocator::purge_if_needed () {
173155 auto now = std::chrono::steady_clock::now ();
174-
175156 bool purge_happened = false ;
176157
177- while (true ) {
178- if (free_objects.empty ()) {
179- break ;
180- }
181-
182- // Has to be copied by value.
183- auto oldest_free_obj = free_objects.front ();
184-
185- std::chrono::duration<double > duration = now - oldest_free_obj.timestamp ;
186- // If the first free object has not decayed, so do the rest free objects.
187- if (duration.count () < DECAY_TIME ) {
188- break ;
189- }
190-
191- switch (free_objects.front ().kind ) {
192- case FreeObjectKind::Buffer: {
193- Logger::debug (" Purging buffer: " + oldest_free_obj.buffer_allocation .tag );
194-
195- free_objects.erase (free_objects.begin ());
196- bytes_allocated -= oldest_free_obj.buffer_allocation .buffer ->get_size ();
197-
198- purge_happened = true ;
199- } break ;
200- case FreeObjectKind::Texture: {
201- Logger::debug (" Purging texture: " + oldest_free_obj.texture_allocation .tag );
202-
203- free_objects.erase (free_objects.begin ());
204- bytes_allocated -= oldest_free_obj.texture_allocation .descriptor .byte_size ();
205-
206- purge_happened = true ;
207- } break ;
208- default :
209- break ;
158+ for (auto it = idle_pool.begin (); it != idle_pool.end ();) {
159+ std::chrono::duration<double > duration = now - it->timestamp ;
160+ if (duration.count () >= DECAY_TIME ) {
161+ if (it->kind == FreeObjectKind::Buffer) {
162+ bytes_allocated -= it->buffer_allocation .buffer ->get_size ();
163+ } else {
164+ bytes_allocated -= it->texture_allocation .descriptor .byte_size ();
165+ }
166+ it = idle_pool.erase (it);
167+ purge_happened = true ;
168+ } else {
169+ ++it;
210170 }
211171 }
212172
213- if (purge_happened) {
214- print_info ();
215- }
173+ if (purge_happened) print_info ();
216174}
217175
218176void GpuMemoryAllocator::print_info () {
219- size_t texture_count = textures_in_use.size ();
220- size_t buffer_count = buffers_in_use.size ();
221- size_t free_object_count = free_objects.size ();
177+ size_t texture_count = active_textures.size ();
178+ size_t buffer_count = active_buffers.size ();
179+
180+ size_t idle_count = idle_pool.size ();
181+ size_t pending_count = 0 ;
182+ for (const auto & bucket : pending_buckets) {
183+ pending_count += bucket.objects .size ();
184+ }
222185
223186 Logger::debug (" Current status: ALLOCATED " + std::to_string (int (bytes_allocated / 1024 .f )) + " KB | COMMITTED " +
224- std::to_string (int (bytes_committed / 1024 .f )) + " KB | Textures " + std::to_string (texture_count) +
225- " | Buffers " + std::to_string (buffer_count) + " | Free objects " +
226- std::to_string (free_object_count ));
187+ std::to_string (int (bytes_committed / 1024 .f )) + " KB | Active textures " + std::to_string (texture_count) +
188+ " | Active buffers " + std::to_string (buffer_count) + " | Free (Idle/Pending) " +
189+ std::to_string (idle_count) + " / " + std::to_string (pending_count ));
227190
228- for (auto & allocation : textures_in_use ) {
229- Logger::debug (" Texture " + std::to_string (allocation.first ) + " : " + allocation.second .tag + " - " +
191+ for (auto & allocation : active_textures ) {
192+ Logger::debug (" Active texture " + std::to_string (allocation.first ) + " : " + allocation.second .tag + " - " +
230193 std::to_string (int (allocation.second .descriptor .byte_size () / 1024 .f )) + " KB" );
231194 }
232195
233- for (auto & allocation : buffers_in_use ) {
234- Logger::debug (" Buffer " + std::to_string (allocation.first ) + " : " + allocation.second .tag + " - " +
196+ for (auto & allocation : active_buffers ) {
197+ Logger::debug (" Active Buffer " + std::to_string (allocation.first ) + " : " + allocation.second .tag + " - " +
235198 std::to_string (int (allocation.second .descriptor .size / 1024 .f )) + " KB" );
236199 }
237200}
0 commit comments