Skip to content

Commit fb635ef

Browse files
authored
VMA Sources (#599)
Ported and C#ified, but without the data structure allocation optimizations of the original
1 parent 0277641 commit fb635ef

22 files changed

+5305
-0
lines changed
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
#pragma warning disable CA1063
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Threading;
6+
using System.Diagnostics;
7+
8+
using Silk.NET.Vulkan;
9+
using Buffer = Silk.NET.Vulkan.Buffer;
10+
using System.Buffers;
11+
12+
namespace VMASharp
13+
{
14+
/// <summary>
15+
/// The object containing details on a suballocation of Vulkan Memory
16+
/// </summary>
17+
public unsafe abstract class Allocation : IDisposable
18+
{
19+
internal VulkanMemoryAllocator Allocator { get; }
20+
21+
protected Vk VkApi => Allocator.VkApi;
22+
23+
protected long size;
24+
protected long alignment;
25+
private int lastUseFrameIndex;
26+
protected int memoryTypeIndex;
27+
protected int mapCount;
28+
private bool LostOrDisposed = false;
29+
30+
/// <summary>
31+
/// Size of this allocation, in bytes.
32+
/// Value never changes, unless allocation is lost.
33+
/// </summary>
34+
public long Size
35+
{
36+
get
37+
{
38+
if (LostOrDisposed || lastUseFrameIndex == Helpers.FrameIndexLost)
39+
{
40+
return 0;
41+
}
42+
43+
return size;
44+
}
45+
}
46+
47+
/// <summary>
48+
/// Memory type index that this allocation is from. Value does not change.
49+
/// </summary>
50+
public int MemoryTypeIndex { get => memoryTypeIndex; }
51+
52+
/// <summary>
53+
/// Handle to Vulkan memory object.
54+
/// Same memory object can be shared by multiple allocations.
55+
/// It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
56+
/// If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
57+
/// </summary>
58+
public abstract DeviceMemory DeviceMemory { get; }
59+
60+
/// <summary>
61+
/// Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
62+
/// It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
63+
/// </summary>
64+
public abstract long Offset { get; internal set; }
65+
66+
internal abstract bool CanBecomeLost { get; }
67+
68+
69+
internal bool IsPersistantMapped
70+
{
71+
get => this.mapCount < 0;
72+
}
73+
74+
internal int LastUseFrameIndex
75+
{
76+
get
77+
{
78+
return this.lastUseFrameIndex;
79+
}
80+
}
81+
82+
internal long Alignment => this.alignment;
83+
84+
public object? UserData { get; set; }
85+
86+
internal Allocation(VulkanMemoryAllocator allocator, int currentFrameIndex)
87+
{
88+
this.Allocator = allocator;
89+
this.lastUseFrameIndex = currentFrameIndex;
90+
}
91+
92+
/// <summary>
93+
/// If this allocation is mapped, returns a pointer to the mapped memory region. Returns Null otherwise.
94+
/// </summary>
95+
public abstract IntPtr MappedData { get; }
96+
97+
public void Dispose()
98+
{
99+
if (!this.LostOrDisposed)
100+
{
101+
this.Allocator.FreeMemory(this);
102+
LostOrDisposed = true;
103+
}
104+
}
105+
106+
public Result BindBufferMemory(Buffer buffer)
107+
{
108+
Debug.Assert(this.Offset >= 0);
109+
110+
return this.Allocator.BindVulkanBuffer(buffer, this.DeviceMemory, this.Offset, null);
111+
}
112+
113+
public unsafe Result BindBufferMemory(Buffer buffer, long allocationLocalOffset, IntPtr pNext)
114+
{
115+
return this.BindBufferMemory(buffer, allocationLocalOffset, (void*)pNext);
116+
}
117+
118+
public unsafe Result BindBufferMemory(Buffer buffer, long allocationLocalOffset, void* pNext = null)
119+
{
120+
if ((ulong)allocationLocalOffset >= (ulong)this.Size)
121+
{
122+
throw new ArgumentOutOfRangeException(nameof(allocationLocalOffset));
123+
}
124+
125+
return this.Allocator.BindVulkanBuffer(buffer, this.DeviceMemory, this.Offset + allocationLocalOffset, pNext);
126+
}
127+
128+
public unsafe Result BindImageMemory(Image image)
129+
{
130+
return this.Allocator.BindVulkanImage(image, this.DeviceMemory, this.Offset, null);
131+
}
132+
133+
public unsafe Result BindImageMemory(Image image, long allocationLocalOffset, IntPtr pNext)
134+
{
135+
return this.BindImageMemory(image, allocationLocalOffset, (void*)pNext);
136+
}
137+
138+
public unsafe Result BindImageMemory(Image image, long allocationLocalOffset, void* pNext = null)
139+
{
140+
if ((ulong)allocationLocalOffset >= (ulong)this.Size)
141+
{
142+
throw new ArgumentOutOfRangeException(nameof(allocationLocalOffset));
143+
}
144+
145+
return this.Allocator.BindVulkanImage(image, this.DeviceMemory, this.Offset + allocationLocalOffset, pNext);
146+
}
147+
148+
internal bool MakeLost(int currentFrame, int frameInUseCount)
149+
{
150+
if (!this.CanBecomeLost)
151+
{
152+
throw new InvalidOperationException("Internal Exception, tried to make an allocation lost that cannot become lost.");
153+
}
154+
155+
int localLastUseFrameIndex = this.lastUseFrameIndex;
156+
157+
while (true)
158+
{
159+
if (localLastUseFrameIndex == Helpers.FrameIndexLost)
160+
{
161+
Debug.Assert(false);
162+
return false;
163+
}
164+
else if (localLastUseFrameIndex + frameInUseCount >= currentFrame)
165+
{
166+
return false;
167+
}
168+
else
169+
{
170+
var tmp = Interlocked.CompareExchange(ref this.lastUseFrameIndex, Helpers.FrameIndexLost, localLastUseFrameIndex);
171+
172+
if (tmp == localLastUseFrameIndex)
173+
{
174+
this.LostOrDisposed = true;
175+
return true;
176+
}
177+
178+
localLastUseFrameIndex = tmp;
179+
}
180+
}
181+
}
182+
183+
public bool TouchAllocation()
184+
{
185+
if (this.LostOrDisposed)
186+
{
187+
return false;
188+
}
189+
190+
int currFrameIndexLoc = this.Allocator.CurrentFrameIndex;
191+
int lastUseFrameIndexLoc = this.lastUseFrameIndex;
192+
193+
if (this.CanBecomeLost)
194+
{
195+
while (true)
196+
{
197+
if (lastUseFrameIndexLoc == Helpers.FrameIndexLost)
198+
{
199+
return false;
200+
}
201+
else if (lastUseFrameIndexLoc == currFrameIndexLoc)
202+
{
203+
return true;
204+
}
205+
206+
lastUseFrameIndexLoc = Interlocked.CompareExchange(ref this.lastUseFrameIndex, currFrameIndexLoc, lastUseFrameIndexLoc);
207+
}
208+
}
209+
else
210+
{
211+
while (true)
212+
{
213+
Debug.Assert(lastUseFrameIndexLoc != Helpers.FrameIndexLost);
214+
215+
if (lastUseFrameIndexLoc == currFrameIndexLoc)
216+
break;
217+
218+
lastUseFrameIndexLoc = Interlocked.CompareExchange(ref this.lastUseFrameIndex, currFrameIndexLoc, lastUseFrameIndexLoc);
219+
}
220+
221+
return true;
222+
}
223+
}
224+
225+
/// <summary>
226+
/// Flushes a specified region of memory
227+
/// </summary>
228+
/// <param name="offset">Offset in this allocation</param>
229+
/// <param name="size">Size of region to flush</param>
230+
/// <returns>The result of the operation</returns>
231+
public Result Flush(long offset, long size)
232+
{
233+
return Allocator.FlushOrInvalidateAllocation(this, offset, size, CacheOperation.Flush);
234+
}
235+
236+
/// <summary>
237+
/// Invalidates a specified region of memory
238+
/// </summary>
239+
/// <param name="offset">Offset in this allocation</param>
240+
/// <param name="size">Size of region to Invalidate</param>
241+
/// <returns>The result of the operation</returns>
242+
public Result Invalidate(long offset, long size)
243+
{
244+
return Allocator.FlushOrInvalidateAllocation(this, offset, size, CacheOperation.Invalidate);
245+
}
246+
247+
public abstract IntPtr Map();
248+
249+
public abstract void Unmap();
250+
251+
public bool TryGetMemory<T>(out Memory<T> memory) where T: unmanaged
252+
{
253+
if (mapCount != 0)
254+
{
255+
int size = checked((int)this.Size);
256+
257+
if (size >= sizeof(T))
258+
{
259+
memory = new UnmanagedMemoryManager<T>((byte*)MappedData, size / sizeof(T)).Memory;
260+
261+
return true;
262+
}
263+
}
264+
265+
memory = Memory<T>.Empty;
266+
return false;
267+
}
268+
269+
public bool TryGetSpan<T>(out Span<T> span) where T: unmanaged
270+
{
271+
if (mapCount != 0)
272+
{
273+
int size = checked((int)this.Size);
274+
275+
if (size >= sizeof(T))
276+
{
277+
span = new Span<T>((void*)MappedData, size / sizeof(T));
278+
279+
return true;
280+
}
281+
}
282+
283+
span = Span<T>.Empty;
284+
return false;
285+
}
286+
287+
private unsafe sealed class UnmanagedMemoryManager<T> : MemoryManager<T> where T: unmanaged
288+
{
289+
private readonly T* Pointer;
290+
private readonly int ElementCount;
291+
292+
public UnmanagedMemoryManager(void* ptr, int elemCount)
293+
{
294+
Pointer = (T*)ptr;
295+
ElementCount = elemCount;
296+
}
297+
298+
protected override void Dispose(bool disposing)
299+
{
300+
}
301+
302+
public override Span<T> GetSpan()
303+
{
304+
return new Span<T>(Pointer, ElementCount);
305+
}
306+
307+
public override MemoryHandle Pin(int elementIndex = 0)
308+
{
309+
return new MemoryHandle(Pointer + elementIndex);
310+
}
311+
312+
public override void Unpin()
313+
{
314+
}
315+
}
316+
}
317+
}

0 commit comments

Comments
 (0)