Skip to content

Commit b39093e

Browse files
committed
Vertex/IndexBuffers
1 parent 87af1a2 commit b39093e

22 files changed

+1317
-171
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Collections.Concurrent;
2+
using Intersect.Client.Framework.Graphics;
3+
using Intersect.Framework;
4+
5+
namespace Intersect.Client.MonoGame.Graphics;
6+
7+
internal partial class MonoRenderer
8+
{
9+
private static Microsoft.Xna.Framework.Graphics.BufferUsage BufferUsageToMonoGameBufferUsage(BufferUsage usage)
10+
{
11+
Microsoft.Xna.Framework.Graphics.BufferUsage platformUsage = usage switch
12+
{
13+
BufferUsage.None => Microsoft.Xna.Framework.Graphics.BufferUsage.None,
14+
BufferUsage.WriteOnly => Microsoft.Xna.Framework.Graphics.BufferUsage.WriteOnly,
15+
_ => throw Exceptions.UnreachableInvalidEnum(usage),
16+
};
17+
return platformUsage;
18+
}
19+
20+
private abstract class GPUBuffer : IGPUBuffer
21+
{
22+
private static readonly ConcurrentDictionary<Type, uint> NextIdLookup = [];
23+
24+
protected GPUBuffer()
25+
{
26+
Id = NextIdLookup.AddOrUpdate(GetType(), _ => 1, (_, nextId) => nextId + 1);
27+
}
28+
29+
public uint Id { get; }
30+
31+
public abstract int Count { get; }
32+
33+
public abstract int SizeBytes { get; }
34+
35+
protected virtual void ReleaseUnmanagedResources()
36+
{
37+
}
38+
39+
40+
protected virtual void ReleaseManagedResources()
41+
{
42+
}
43+
44+
private void Dispose(bool disposing)
45+
{
46+
ReleaseUnmanagedResources();
47+
if (disposing)
48+
{
49+
ReleaseManagedResources();
50+
}
51+
}
52+
53+
public void Dispose()
54+
{
55+
Dispose(true);
56+
GC.SuppressFinalize(this);
57+
}
58+
59+
~GPUBuffer()
60+
{
61+
Dispose(false);
62+
}
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
using System.Collections.Concurrent;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Runtime.InteropServices;
4+
using Intersect.Client.Framework.Graphics;
5+
using Intersect.Core;
6+
using Intersect.Framework;
7+
using Intersect.Framework.Reflection;
8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Xna.Framework.Graphics;
10+
using BufferUsage = Intersect.Client.Framework.Graphics.BufferUsage;
11+
using PrimitiveType = Intersect.Client.Framework.Graphics.PrimitiveType;
12+
13+
namespace Intersect.Client.MonoGame.Graphics;
14+
15+
internal partial class MonoRenderer
16+
{
17+
private readonly ConcurrentDictionary<Microsoft.Xna.Framework.Graphics.IndexBuffer, IIndexBuffer> _allocatedIndexBuffers = [];
18+
private long _allocatedIndexBuffersSize;
19+
20+
private bool AddAllocatedIndexBuffer(Microsoft.Xna.Framework.Graphics.IndexBuffer platformBuffer, IIndexBuffer buffer)
21+
{
22+
if (!_allocatedIndexBuffers.TryAdd(platformBuffer, buffer))
23+
{
24+
return false;
25+
}
26+
27+
MarkAllocated(buffer);
28+
29+
_allocatedIndexBuffersSize += MeasureDataSize(platformBuffer);
30+
return true;
31+
}
32+
33+
private bool RemoveAllocatedIndexBuffer(Microsoft.Xna.Framework.Graphics.IndexBuffer platformBuffer, [NotNullWhen(true)] out IIndexBuffer? buffer)
34+
{
35+
if (!_allocatedIndexBuffers.TryRemove(platformBuffer, out buffer))
36+
{
37+
return false;
38+
}
39+
40+
_allocatedIndexBuffersSize -= MeasureDataSize(platformBuffer);
41+
return true;
42+
}
43+
44+
private static long MeasureDataSize(Microsoft.Xna.Framework.Graphics.IndexBuffer platformBuffer)
45+
{
46+
return platformBuffer.IndexCount *
47+
platformBuffer.IndexElementSize switch
48+
{
49+
IndexElementSize.SixteenBits => 2,
50+
IndexElementSize.ThirtyTwoBits => 4,
51+
_ => throw Exceptions.UnreachableInvalidEnum(platformBuffer.IndexElementSize),
52+
};
53+
}
54+
55+
public override IIndexBuffer CreateIndexBuffer<TIndex>(int count, BufferUsage usage = BufferUsage.None, bool dynamic = false) where TIndex : struct
56+
{
57+
Microsoft.Xna.Framework.Graphics.IndexBuffer platformBuffer;
58+
var platformUsage = BufferUsageToMonoGameBufferUsage(usage);
59+
60+
if (dynamic)
61+
{
62+
platformBuffer = new DynamicIndexBuffer(_graphicsDevice, typeof(TIndex), count, platformUsage);
63+
}
64+
else
65+
{
66+
platformBuffer = new Microsoft.Xna.Framework.Graphics.IndexBuffer(
67+
_graphicsDevice,
68+
typeof(TIndex),
69+
count,
70+
platformUsage
71+
);
72+
}
73+
74+
IndexBuffer buffer = new(platformBuffer, typeof(TIndex));
75+
platformBuffer.Disposing += IndexBufferOnDisposing;
76+
if (AddAllocatedIndexBuffer(platformBuffer, buffer))
77+
{
78+
return buffer;
79+
}
80+
81+
buffer.Dispose();
82+
throw new InvalidOperationException("Unable to track index buffer");
83+
}
84+
85+
private void IndexBufferOnDisposing(object? sender, EventArgs args)
86+
{
87+
if (sender is Microsoft.Xna.Framework.Graphics.IndexBuffer platformBuffer)
88+
{
89+
OnPlatformIndexBufferDisposal(platformBuffer);
90+
return;
91+
}
92+
93+
ApplicationContext.CurrentContext.Logger.LogError(
94+
"Received disposal event but it was not from an instance of {ExpectedType} ({ActualType})",
95+
typeof(Microsoft.Xna.Framework.Graphics.IndexBuffer).GetName(qualified: true),
96+
sender?.GetType().GetName(qualified: true) ?? "null"
97+
);
98+
}
99+
100+
private void OnPlatformIndexBufferDisposal(Microsoft.Xna.Framework.Graphics.IndexBuffer platformBuffer)
101+
{
102+
if (RemoveAllocatedIndexBuffer(platformBuffer, out var buffer))
103+
{
104+
MarkFreed(buffer);
105+
}
106+
else
107+
{
108+
ApplicationContext.CurrentContext.Logger.LogError(
109+
"Failed to remove platform buffer from allocations, is it not tracked? '{BufferId}'",
110+
platformBuffer.ToString()
111+
);
112+
}
113+
}
114+
115+
private sealed class IndexBuffer(Microsoft.Xna.Framework.Graphics.IndexBuffer platformBuffer, Type indexType) : GPUBuffer, IIndexBuffer
116+
{
117+
internal readonly Microsoft.Xna.Framework.Graphics.IndexBuffer PlatformBuffer = platformBuffer;
118+
119+
public Type IndexType { get; } = indexType;
120+
121+
public override int Count => PlatformBuffer.IndexCount;
122+
123+
public override int SizeBytes => (int)MeasureDataSize(PlatformBuffer);
124+
125+
protected override void ReleaseManagedResources()
126+
{
127+
base.ReleaseManagedResources();
128+
129+
if (!PlatformBuffer.IsDisposed)
130+
{
131+
PlatformBuffer.Dispose();
132+
}
133+
}
134+
135+
public bool GetData<TIndex>(TIndex[] destination) where TIndex : struct =>
136+
GetData(bufferOffset: 0, destination: destination, destinationOffset: 0, length: destination.Length);
137+
138+
public bool GetData<TIndex>(TIndex[] destination, int destinationOffset, int length) where TIndex : struct =>
139+
GetData(bufferOffset: 0, destination: destination, destinationOffset: destinationOffset, length: length);
140+
141+
public bool GetData<TIndex>(int bufferOffset, TIndex[] destination, int destinationOffset, int length)
142+
where TIndex : struct
143+
{
144+
try
145+
{
146+
PlatformBuffer.GetData(
147+
offsetInBytes: bufferOffset,
148+
data: destination,
149+
startIndex: destinationOffset,
150+
elementCount: length
151+
);
152+
return true;
153+
}
154+
catch (Exception exception)
155+
{
156+
ApplicationContext.CurrentContext.Logger.LogError(
157+
exception,
158+
"Failed to get data for {BufferType} {Id}",
159+
nameof(IIndexBuffer),
160+
Id
161+
);
162+
return false;
163+
}
164+
}
165+
166+
public bool SetData<TIndex>(TIndex[] data) where TIndex : struct => SetData(
167+
destinationOffset: 0,
168+
data: data,
169+
sourceOffset: 0,
170+
length: data.Length,
171+
bufferWriteMode: BufferWriteMode.Overwrite
172+
);
173+
174+
public bool SetData<TIndex>(TIndex[] data, int sourceOffset, int length)
175+
where TIndex : struct => SetData(
176+
destinationOffset: 0,
177+
data: data,
178+
sourceOffset: sourceOffset,
179+
length: length,
180+
bufferWriteMode: BufferWriteMode.Overwrite
181+
);
182+
183+
public bool SetData<TIndex>(int destinationOffset, TIndex[] data, int sourceOffset, int length)
184+
where TIndex : struct => SetData(
185+
destinationOffset: destinationOffset,
186+
data: data,
187+
sourceOffset: sourceOffset,
188+
length: length,
189+
bufferWriteMode: BufferWriteMode.Overwrite
190+
);
191+
192+
public bool SetData<TIndex>(
193+
int destinationOffset,
194+
TIndex[] data,
195+
int sourceOffset,
196+
int length,
197+
BufferWriteMode bufferWriteMode
198+
) where TIndex : struct
199+
{
200+
if (bufferWriteMode != BufferWriteMode.Overwrite)
201+
{
202+
if (PlatformBuffer is not DynamicIndexBuffer dynamicBuffer)
203+
{
204+
ApplicationContext.CurrentContext.Logger.LogError(
205+
"{BufferWriteMode} is only supported on dynamic buffers",
206+
bufferWriteMode
207+
);
208+
return false;
209+
}
210+
211+
SetDataOptions setDataOptions = bufferWriteMode switch
212+
{
213+
// ReSharper disable once UnreachableSwitchArmDueToIntegerAnalysis
214+
BufferWriteMode.Overwrite => SetDataOptions.None,
215+
BufferWriteMode.Discard => SetDataOptions.Discard,
216+
BufferWriteMode.NoOverwrite => SetDataOptions.None,
217+
_ => throw Exceptions.UnreachableInvalidEnum(value: bufferWriteMode),
218+
};
219+
dynamicBuffer.SetData(
220+
offsetInBytes: destinationOffset,
221+
data: data,
222+
startIndex: sourceOffset,
223+
elementCount: length,
224+
options: setDataOptions
225+
);
226+
}
227+
else
228+
{
229+
PlatformBuffer.SetData(
230+
offsetInBytes: destinationOffset,
231+
data: data,
232+
startIndex: sourceOffset,
233+
elementCount: length
234+
);
235+
}
236+
237+
return true;
238+
}
239+
}
240+
}

0 commit comments

Comments
 (0)