Skip to content

Commit 9637899

Browse files
Use safe handle refs for WebGPU native calls via using
1 parent e48c02c commit 9637899

File tree

4 files changed

+194
-170
lines changed

4 files changed

+194
-170
lines changed

src/ImageSharp.Drawing.WebGPU/WebGPUDrawingBackend.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,11 @@ internal static bool TryCreateCompositionTexture(
310310
SampleCount = 1
311311
};
312312

313-
texture = flushContext.Api.DeviceCreateTexture(flushContext.Device, in textureDescriptor);
313+
using (WebGPUHandle.HandleReference deviceReference = flushContext.DeviceHandle.AcquireReference())
314+
{
315+
texture = flushContext.Api.DeviceCreateTexture((Device*)deviceReference.Handle, in textureDescriptor);
316+
}
317+
314318
if (texture is null)
315319
{
316320
error = "Failed to create WebGPU composition texture.";
@@ -402,7 +406,11 @@ internal static bool TrySubmit(WebGPUFlushContext flushContext)
402406
return false;
403407
}
404408

405-
flushContext.Api.QueueSubmit(flushContext.Queue, 1, ref commandBuffer);
409+
using (WebGPUHandle.HandleReference queueReference = flushContext.QueueHandle.AcquireReference())
410+
{
411+
flushContext.Api.QueueSubmit((Queue*)queueReference.Handle, 1, ref commandBuffer);
412+
}
413+
406414
flushContext.Api.CommandBufferRelease(commandBuffer);
407415
commandBuffer = null;
408416
flushContext.Api.CommandEncoderRelease(commandEncoder);

src/ImageSharp.Drawing.WebGPU/WebGPUFlushContext.cs

Lines changed: 33 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,6 @@ internal enum CompositePipelineBlendMode
2828
/// </summary>
2929
internal sealed unsafe class WebGPUFlushContext : IDisposable
3030
{
31-
private bool disposed;
32-
private bool ownsTargetTexture;
33-
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;
4231
private readonly List<nint> transientBindGroups = [];
4332
private readonly List<nint> transientBuffers = [];
4433
private readonly List<nint> transientTextureViews = [];
@@ -49,6 +38,8 @@ internal sealed unsafe class WebGPUFlushContext : IDisposable
4938
// Handles are released when this flush context is disposed.
5039
private readonly Dictionary<Image, nint> cachedSourceTextureViews = new(ReferenceEqualityComparer.Instance);
5140

41+
private bool disposed;
42+
5243
private WebGPUFlushContext(
5344
WebGPU api,
5445
Wgpu wgpuExtension,
@@ -63,10 +54,10 @@ private WebGPUFlushContext(
6354
{
6455
this.Api = api;
6556
this.WgpuExtension = wgpuExtension;
66-
this.deviceHandle = deviceHandle;
67-
this.queueHandle = queueHandle;
68-
this.targetTextureHandle = targetTextureHandle;
69-
this.targetTextureViewHandle = targetTextureViewHandle;
57+
this.DeviceHandle = deviceHandle;
58+
this.QueueHandle = queueHandle;
59+
this.TargetTextureHandle = targetTextureHandle;
60+
this.TargetTextureViewHandle = targetTextureViewHandle;
7061
this.TargetBounds = targetBounds;
7162
this.TextureFormat = textureFormat;
7263
this.MemoryAllocator = memoryAllocator;
@@ -84,14 +75,32 @@ private WebGPUFlushContext(
8475
public WebGPU Api { get; }
8576

8677
/// <summary>
87-
/// Gets the device used to create and execute GPU resources.
78+
/// Gets the safe handle for the device used to create and execute GPU resources.
79+
/// Acquire a scoped reference with <see cref="WebGPUHandle.AcquireReference"/> for the
80+
/// duration of any native call that uses the underlying pointer.
81+
/// </summary>
82+
public WebGPUDeviceHandle DeviceHandle { get; }
83+
84+
/// <summary>
85+
/// Gets the safe handle for the queue used to submit GPU work.
86+
/// Acquire a scoped reference with <see cref="WebGPUHandle.AcquireReference"/> for the
87+
/// duration of any native call that uses the underlying pointer.
8888
/// </summary>
89-
public Device* Device { get; private set; }
89+
public WebGPUQueueHandle QueueHandle { get; }
9090

9191
/// <summary>
92-
/// Gets the queue used to submit GPU work.
92+
/// Gets the safe handle for the target texture receiving render/composite output.
93+
/// Acquire a scoped reference with <see cref="WebGPUHandle.AcquireReference"/> for the
94+
/// duration of any native call that uses the underlying pointer.
9395
/// </summary>
94-
public Queue* Queue { get; private set; }
96+
public WebGPUTextureHandle TargetTextureHandle { get; }
97+
98+
/// <summary>
99+
/// Gets the safe handle for the texture view used when binding the target texture.
100+
/// Acquire a scoped reference with <see cref="WebGPUHandle.AcquireReference"/> for the
101+
/// duration of any native call that uses the underlying pointer.
102+
/// </summary>
103+
public WebGPUTextureViewHandle TargetTextureViewHandle { get; }
95104

96105
/// <summary>
97106
/// Gets the target bounds for this flush context.
@@ -113,16 +122,6 @@ private WebGPUFlushContext(
113122
/// </summary>
114123
public WebGPURuntime.DeviceSharedState DeviceState { get; }
115124

116-
/// <summary>
117-
/// Gets the target texture receiving render/composite output.
118-
/// </summary>
119-
public Texture* TargetTexture { get; private set; }
120-
121-
/// <summary>
122-
/// Gets the texture view used when binding the target texture.
123-
/// </summary>
124-
public TextureView* TargetView { get; private set; }
125-
126125
/// <summary>
127126
/// Gets the shared instance-data buffer used for parameter uploads.
128127
/// </summary>
@@ -198,7 +197,7 @@ private WebGPUFlushContext(
198197
return null;
199198
}
200199

201-
WebGPUFlushContext context = new(
200+
return new WebGPUFlushContext(
202201
api,
203202
WebGPURuntime.GetWgpuExtension(),
204203
nativeCapability.DeviceHandle,
@@ -209,21 +208,6 @@ private WebGPUFlushContext(
209208
textureFormat,
210209
memoryAllocator,
211210
deviceState);
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-
226-
return context;
227211
}
228212

229213
/// <summary>
@@ -253,7 +237,8 @@ public bool EnsureInstanceBufferCapacity(nuint requiredBytes, nuint minimumCapac
253237
Size = targetSize
254238
};
255239

256-
this.InstanceBuffer = this.Api.DeviceCreateBuffer(this.Device, in descriptor);
240+
using WebGPUHandle.HandleReference deviceReference = this.DeviceHandle.AcquireReference();
241+
this.InstanceBuffer = this.Api.DeviceCreateBuffer((Device*)deviceReference.Handle, in descriptor);
257242
if (this.InstanceBuffer is null)
258243
{
259244
return false;
@@ -275,7 +260,8 @@ public bool EnsureCommandEncoder()
275260
}
276261

277262
CommandEncoderDescriptor descriptor = default;
278-
this.CommandEncoder = this.Api.DeviceCreateCommandEncoder(this.Device, in descriptor);
263+
using WebGPUHandle.HandleReference deviceReference = this.DeviceHandle.AcquireReference();
264+
this.CommandEncoder = this.Api.DeviceCreateCommandEncoder((Device*)deviceReference.Handle, in descriptor);
279265
return this.CommandEncoder is not null;
280266
}
281267

@@ -469,16 +455,6 @@ public void Dispose()
469455

470456
this.InstanceBufferWriteOffset = 0;
471457

472-
if (this.ownsTargetView && this.TargetView is not null)
473-
{
474-
this.Api.TextureViewRelease(this.TargetView);
475-
}
476-
477-
if (this.ownsTargetTexture && this.TargetTexture is not null)
478-
{
479-
this.Api.TextureRelease(this.TargetTexture);
480-
}
481-
482458
for (int i = 0; i < this.transientBindGroups.Count; i++)
483459
{
484460
this.Api.BindGroupRelease((BindGroup*)this.transientBindGroups[i]);
@@ -507,52 +483,9 @@ public void Dispose()
507483
// Cache entries point to transient texture views that are released above.
508484
this.cachedSourceTextureViews.Clear();
509485

510-
this.TargetView = null;
511-
this.TargetTexture = null;
512-
this.ownsTargetView = false;
513-
this.ownsTargetTexture = false;
514-
this.DisposeNativeHandleReferences();
515-
516486
this.disposed = true;
517487
}
518488

519-
/// <summary>
520-
/// Adopts the texture and texture view provided by a native WebGPU surface capability.
521-
/// </summary>
522-
/// <returns><see langword="true"/> when all required native handles were acquired successfully; otherwise, <see langword="false"/>.</returns>
523-
private bool InitializeNativeTarget()
524-
{
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.Value.Handle;
533-
this.Queue = (Queue*)this.queueReference.Value.Handle;
534-
this.TargetTexture = (Texture*)this.targetTextureReference.Value.Handle;
535-
this.TargetView = (TextureView*)this.targetTextureViewReference.Value.Handle;
536-
this.ownsTargetTexture = false;
537-
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;
554-
}
555-
556489
/// <summary>
557490
/// Tries to obtain a native WebGPU surface capability from the canvas frame.
558491
/// </summary>

0 commit comments

Comments
 (0)