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