@@ -31,6 +31,14 @@ internal sealed unsafe class WebGPUFlushContext : IDisposable
3131 private bool disposed ;
3232 private bool ownsTargetTexture ;
3333 private bool ownsTargetView ;
34+ private readonly WebGPUDeviceHandle deviceHandle ;
35+ private readonly WebGPUQueueHandle queueHandle ;
36+ private readonly WebGPUTextureHandle targetTextureHandle ;
37+ private readonly WebGPUTextureViewHandle targetTextureViewHandle ;
38+ private WebGPUHandle . HandleReference ? deviceReference ;
39+ private WebGPUHandle . HandleReference ? queueReference ;
40+ private WebGPUHandle . HandleReference ? targetTextureReference ;
41+ private WebGPUHandle . HandleReference ? targetTextureViewReference ;
3442 private readonly List < nint > transientBindGroups = [ ] ;
3543 private readonly List < nint > transientBuffers = [ ] ;
3644 private readonly List < nint > transientTextureViews = [ ] ;
@@ -44,17 +52,21 @@ internal sealed unsafe class WebGPUFlushContext : IDisposable
4452 private WebGPUFlushContext (
4553 WebGPU api ,
4654 Wgpu wgpuExtension ,
47- Device * device ,
48- Queue * queue ,
55+ WebGPUDeviceHandle deviceHandle ,
56+ WebGPUQueueHandle queueHandle ,
57+ WebGPUTextureHandle targetTextureHandle ,
58+ WebGPUTextureViewHandle targetTextureViewHandle ,
4959 in Rectangle targetBounds ,
5060 TextureFormat textureFormat ,
5161 MemoryAllocator memoryAllocator ,
5262 WebGPURuntime . DeviceSharedState deviceState )
5363 {
5464 this . Api = api ;
5565 this . WgpuExtension = wgpuExtension ;
56- this . Device = device ;
57- this . Queue = queue ;
66+ this . deviceHandle = deviceHandle ;
67+ this . queueHandle = queueHandle ;
68+ this . targetTextureHandle = targetTextureHandle ;
69+ this . targetTextureViewHandle = targetTextureViewHandle ;
5870 this . TargetBounds = targetBounds ;
5971 this . TextureFormat = textureFormat ;
6072 this . MemoryAllocator = memoryAllocator ;
@@ -74,12 +86,12 @@ private WebGPUFlushContext(
7486 /// <summary>
7587 /// Gets the device used to create and execute GPU resources.
7688 /// </summary>
77- public Device * Device { get ; }
89+ public Device * Device { get ; private set ; }
7890
7991 /// <summary>
8092 /// Gets the queue used to submit GPU work.
8193 /// </summary>
82- public Queue * Queue { get ; }
94+ public Queue * Queue { get ; private set ; }
8395
8496 /// <summary>
8597 /// Gets the target bounds for this flush context.
@@ -168,12 +180,10 @@ private WebGPUFlushContext(
168180 }
169181
170182 WebGPU api = WebGPURuntime . GetApi ( ) ;
171- Device * device = ( Device * ) nativeCapability . Device ;
172- Queue * queue = ( Queue * ) nativeCapability . Queue ;
173183 TextureFormat textureFormat = WebGPUTextureFormatMapper . ToSilk ( nativeCapability . TargetFormat ) ;
174184 Rectangle bounds = frame . Bounds ;
175185 Rectangle nativeBounds = new ( 0 , 0 , nativeCapability . Width , nativeCapability . Height ) ;
176- WebGPURuntime . DeviceSharedState deviceState = WebGPURuntime . GetOrCreateDeviceState ( api , device ) ;
186+ WebGPURuntime . DeviceSharedState deviceState = WebGPURuntime . GetOrCreateDeviceState ( api , nativeCapability . DeviceHandle ) ;
177187
178188 if ( requiredFeature != FeatureName . Undefined && ! deviceState . HasFeature ( requiredFeature ) )
179189 {
@@ -191,13 +201,28 @@ private WebGPUFlushContext(
191201 WebGPUFlushContext context = new (
192202 api ,
193203 WebGPURuntime . GetWgpuExtension ( ) ,
194- device ,
195- queue ,
204+ nativeCapability . DeviceHandle ,
205+ nativeCapability . QueueHandle ,
206+ nativeCapability . TargetTextureHandle ,
207+ nativeCapability . TargetTextureViewHandle ,
196208 in bounds ,
197209 textureFormat ,
198210 memoryAllocator ,
199211 deviceState ) ;
200- context . InitializeNativeTarget ( nativeCapability ) ;
212+ try
213+ {
214+ if ( ! context . InitializeNativeTarget ( ) )
215+ {
216+ context . Dispose ( ) ;
217+ return null ;
218+ }
219+ }
220+ catch
221+ {
222+ context . Dispose ( ) ;
223+ throw ;
224+ }
225+
201226 return context ;
202227 }
203228
@@ -486,20 +511,46 @@ public void Dispose()
486511 this . TargetTexture = null ;
487512 this . ownsTargetView = false ;
488513 this . ownsTargetTexture = false ;
514+ this . DisposeNativeHandleReferences ( ) ;
489515
490516 this . disposed = true ;
491517 }
492518
493519 /// <summary>
494520 /// Adopts the texture and texture view provided by a native WebGPU surface capability.
495521 /// </summary>
496- /// <param name="capability">The native surface capability describing the externally owned target .</param >
497- private void InitializeNativeTarget ( WebGPUSurfaceCapability capability )
522+ /// <returns><see langword="true"/> when all required native handles were acquired successfully; otherwise, <see langword="false"/> .</returns >
523+ private bool InitializeNativeTarget ( )
498524 {
499- this . TargetTexture = ( Texture * ) capability . TargetTexture ;
500- this . TargetView = ( TextureView * ) capability . TargetTextureView ;
525+ // The flush context caches raw native pointers and reuses them across the full flush,
526+ // so each safe handle must stay add-ref'd until the context is disposed. These fields are
527+ // assigned immediately so Dispose can unwind partial initialization if a later acquire throws.
528+ this . deviceReference = this . deviceHandle . AcquireReference ( ) ;
529+ this . queueReference = this . queueHandle . AcquireReference ( ) ;
530+ this . targetTextureReference = this . targetTextureHandle . AcquireReference ( ) ;
531+ this . targetTextureViewReference = this . targetTextureViewHandle . AcquireReference ( ) ;
532+ this . Device = ( Device * ) this . deviceReference . Handle ;
533+ this . Queue = ( Queue * ) this . queueReference . Handle ;
534+ this . TargetTexture = ( Texture * ) this . targetTextureReference . Handle ;
535+ this . TargetView = ( TextureView * ) this . targetTextureViewReference . Handle ;
501536 this . ownsTargetTexture = false ;
502537 this . ownsTargetView = false ;
538+ return true ;
539+ }
540+
541+ /// <summary>
542+ /// Releases the scoped safe-handle references that keep the cached raw pointers valid for this flush.
543+ /// </summary>
544+ private void DisposeNativeHandleReferences ( )
545+ {
546+ this . targetTextureViewReference ? . Dispose ( ) ;
547+ this . targetTextureViewReference = null ;
548+ this . targetTextureReference ? . Dispose ( ) ;
549+ this . targetTextureReference = null ;
550+ this . queueReference ? . Dispose ( ) ;
551+ this . queueReference = null ;
552+ this . deviceReference ? . Dispose ( ) ;
553+ this . deviceReference = null ;
503554 }
504555
505556 /// <summary>
@@ -518,9 +569,9 @@ private void InitializeNativeTarget(WebGPUSurfaceCapability capability)
518569 return null ;
519570 }
520571
521- if ( capability . Device == 0 ||
522- capability . Queue == 0 ||
523- capability . TargetTextureView == 0 ||
572+ if ( capability . DeviceHandle . IsInvalid ||
573+ capability . QueueHandle . IsInvalid ||
574+ capability . TargetTextureViewHandle . IsInvalid ||
524575 WebGPUTextureFormatMapper . ToSilk ( capability . TargetFormat ) != expectedTextureFormat )
525576 {
526577 return null ;
0 commit comments