Skip to content

Commit 681b5a4

Browse files
Refactor WebGPU resource allocation to throw
1 parent 76ff622 commit 681b5a4

3 files changed

Lines changed: 44 additions & 104 deletions

File tree

Lines changed: 18 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4-
using System.Diagnostics.CodeAnalysis;
54
using Silk.NET.WebGPU;
65
using SixLabors.ImageSharp.PixelFormats;
76

@@ -13,66 +12,45 @@ namespace SixLabors.ImageSharp.Drawing.Processing.Backends;
1312
internal static unsafe class WebGPURenderTargetAllocation
1413
{
1514
/// <summary>
16-
/// Tries to allocate a WebGPU render target for the specified pixel type.
15+
/// Allocates a WebGPU render target for the specified pixel type.
1716
/// </summary>
1817
/// <typeparam name="TPixel">The pixel format.</typeparam>
1918
/// <param name="api">The WebGPU API instance used to allocate native resources.</param>
2019
/// <param name="deviceHandle">The wrapped <c>WGPUDevice*</c> handle.</param>
2120
/// <param name="queueHandle">The wrapped <c>WGPUQueue*</c> handle.</param>
2221
/// <param name="width">The texture width in pixels.</param>
2322
/// <param name="height">The texture height in pixels.</param>
24-
/// <param name="surface">Receives the native surface wrapping the allocated texture.</param>
2523
/// <param name="textureHandle">Receives the allocated wrapped <c>WGPUTexture*</c> handle.</param>
2624
/// <param name="textureViewHandle">Receives the allocated wrapped <c>WGPUTextureView*</c> handle.</param>
2725
/// <param name="formatId">Receives the allocated texture format identifier.</param>
28-
/// <param name="error">Receives the failure reason when allocation fails.</param>
29-
/// <returns><see langword="true"/> when allocation succeeds; otherwise <see langword="false"/>.</returns>
30-
internal static bool TryCreateRenderTarget<TPixel>(
26+
/// <returns>The native surface wrapping the allocated texture.</returns>
27+
internal static NativeSurface CreateRenderTarget<TPixel>(
3128
WebGPU api,
3229
WebGPUDeviceHandle deviceHandle,
3330
WebGPUQueueHandle queueHandle,
3431
int width,
3532
int height,
36-
out NativeSurface surface,
37-
[NotNullWhen(true)] out WebGPUTextureHandle? textureHandle,
38-
[NotNullWhen(true)] out WebGPUTextureViewHandle? textureViewHandle,
39-
out WebGPUTextureFormatId formatId,
40-
out string error)
33+
out WebGPUTextureHandle textureHandle,
34+
out WebGPUTextureViewHandle textureViewHandle,
35+
out WebGPUTextureFormatId formatId)
4136
where TPixel : unmanaged, IPixel<TPixel>
4237
{
43-
surface = new NativeSurface(TPixel.GetPixelTypeInfo());
44-
textureHandle = null;
45-
textureViewHandle = null;
46-
formatId = default;
47-
4838
if (deviceHandle.IsInvalid)
4939
{
50-
error = "Device handle must be non-zero.";
51-
return false;
40+
throw new InvalidOperationException("The WebGPU device handle is invalid.");
5241
}
5342

5443
if (queueHandle.IsInvalid)
5544
{
56-
error = "Queue handle must be non-zero.";
57-
return false;
58-
}
59-
60-
if (width <= 0)
61-
{
62-
error = "Width must be greater than zero.";
63-
return false;
45+
throw new InvalidOperationException("The WebGPU queue handle is invalid.");
6446
}
6547

66-
if (height <= 0)
67-
{
68-
error = "Height must be greater than zero.";
69-
return false;
70-
}
48+
Guard.MustBeGreaterThan(width, 0, nameof(width));
49+
Guard.MustBeGreaterThan(height, 0, nameof(height));
7150

7251
if (!WebGPUDrawingBackend.TryGetCompositeTextureFormat<TPixel>(out formatId, out FeatureName requiredFeature))
7352
{
74-
error = $"Pixel type '{typeof(TPixel).Name}' is not supported by the WebGPU backend.";
75-
return false;
53+
throw new NotSupportedException($"Pixel type '{typeof(TPixel).Name}' is not supported by the WebGPU backend.");
7654
}
7755

7856
using WebGPUHandle.HandleReference deviceReference = deviceHandle.AcquireReference();
@@ -81,8 +59,7 @@ internal static bool TryCreateRenderTarget<TPixel>(
8159
if (requiredFeature != FeatureName.Undefined &&
8260
!WebGPURuntime.GetOrCreateDeviceState(api, deviceHandle).HasFeature(requiredFeature))
8361
{
84-
error = $"Device does not support required feature '{requiredFeature}' for pixel type '{typeof(TPixel).Name}'.";
85-
return false;
62+
throw new NotSupportedException($"The WebGPU device does not support required feature '{requiredFeature}' for pixel type '{typeof(TPixel).Name}'.");
8663
}
8764

8865
TextureFormat textureFormat = WebGPUTextureFormatMapper.ToSilk(formatId);
@@ -99,8 +76,7 @@ internal static bool TryCreateRenderTarget<TPixel>(
9976
Texture* texture = api.DeviceCreateTexture(device, in textureDescriptor);
10077
if (texture is null)
10178
{
102-
error = "WebGPU.DeviceCreateTexture returned null.";
103-
return false;
79+
throw new InvalidOperationException("The WebGPU device could not create a render-target texture.");
10480
}
10581

10682
TextureViewDescriptor textureViewDescriptor = new()
@@ -118,8 +94,7 @@ internal static bool TryCreateRenderTarget<TPixel>(
11894
if (textureView is null)
11995
{
12096
api.TextureRelease(texture);
121-
error = "WebGPU.TextureCreateView returned null.";
122-
return false;
97+
throw new InvalidOperationException("The WebGPU device could not create a render-target texture view.");
12398
}
12499

125100
WebGPUTextureHandle? createdTextureHandle = null;
@@ -128,7 +103,7 @@ internal static bool TryCreateRenderTarget<TPixel>(
128103
{
129104
createdTextureHandle = new WebGPUTextureHandle(api, (nint)texture, ownsHandle: true);
130105
createdTextureViewHandle = new WebGPUTextureViewHandle(api, (nint)textureView, ownsHandle: true);
131-
surface = WebGPUNativeSurfaceFactory.Create<TPixel>(
106+
NativeSurface surface = WebGPUNativeSurfaceFactory.Create<TPixel>(
132107
deviceHandle,
133108
queueHandle,
134109
createdTextureHandle,
@@ -138,10 +113,9 @@ internal static bool TryCreateRenderTarget<TPixel>(
138113
height);
139114
textureHandle = createdTextureHandle;
140115
textureViewHandle = createdTextureViewHandle;
141-
error = string.Empty;
142-
return true;
116+
return surface;
143117
}
144-
catch (Exception ex)
118+
catch
145119
{
146120
createdTextureViewHandle?.Dispose();
147121
createdTextureHandle?.Dispose();
@@ -156,8 +130,7 @@ internal static bool TryCreateRenderTarget<TPixel>(
156130
api.TextureRelease(texture);
157131
}
158132

159-
error = ex.Message;
160-
return false;
133+
throw;
161134
}
162135
}
163136
}

src/ImageSharp.Drawing.WebGPU/WebGPURenderTarget{TPixel}.cs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,15 @@ private WebGPURenderTarget(WebGPUDeviceContext<TPixel> graphics, bool ownsGraphi
5555
graphics.ThrowIfDisposed();
5656

5757
WebGPU api = WebGPURuntime.GetApi();
58-
if (!WebGPURenderTargetAllocation.TryCreateRenderTarget<TPixel>(
59-
api,
60-
graphics.DeviceHandle,
61-
graphics.QueueHandle,
62-
width,
63-
height,
64-
out NativeSurface surface,
65-
out WebGPUTextureHandle? textureHandle,
66-
out WebGPUTextureViewHandle? textureViewHandle,
67-
out WebGPUTextureFormatId format,
68-
out string allocationError))
69-
{
70-
throw new InvalidOperationException(allocationError);
71-
}
58+
NativeSurface surface = WebGPURenderTargetAllocation.CreateRenderTarget<TPixel>(
59+
api,
60+
graphics.DeviceHandle,
61+
graphics.QueueHandle,
62+
width,
63+
height,
64+
out WebGPUTextureHandle textureHandle,
65+
out WebGPUTextureViewHandle textureViewHandle,
66+
out WebGPUTextureFormatId format);
7267

7368
this.TextureHandle = textureHandle;
7469
this.TextureViewHandle = textureViewHandle;

src/ImageSharp.Drawing.WebGPU/WebGPUSurfaceResources{TPixel}.cs

Lines changed: 17 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -429,22 +429,15 @@ private static DeviceResources CreateDeviceResources(
429429

430430
try
431431
{
432-
if (!TryRequestAdapter(api, instance, surface, out adapter, out string? adapterError))
433-
{
434-
throw new InvalidOperationException(adapterError);
435-
}
436-
432+
adapter = RequestAdapter(api, instance, surface);
437433
adapterHandle = new WebGPUAdapterHandle(api, (nint)adapter, ownsHandle: true);
438-
if (!TryRequestDevice(api, adapter, requiredFeature, out Device* device, out string? deviceError))
439-
{
440-
throw new InvalidOperationException(deviceError);
441-
}
442434

435+
Device* device = RequestDevice(api, adapter, requiredFeature);
443436
deviceHandle = new WebGPUDeviceHandle(api, (nint)device, ownsHandle: true);
444437
Queue* queue = api.DeviceGetQueue(device);
445438
if (queue is null)
446439
{
447-
throw new InvalidOperationException("WebGPU queue acquisition failed.");
440+
throw new InvalidOperationException("The WebGPU device did not provide a default queue.");
448441
}
449442

450443
queueHandle = new WebGPUQueueHandle(api, (nint)queue, ownsHandle: true);
@@ -520,21 +513,16 @@ private void RecoverDeviceResources(WebGPUPresentMode presentMode, Size framebuf
520513
}
521514

522515
/// <summary>
523-
/// Requests a high-performance adapter compatible with <paramref name="surface"/> and waits synchronously on the
524-
/// asynchronous callback, up to <see cref="CallbackTimeoutMilliseconds"/>.
516+
/// Requests a high-performance adapter compatible with <paramref name="surface"/>.
525517
/// </summary>
526518
/// <param name="api">The shared WebGPU API loader.</param>
527519
/// <param name="instance">The WebGPU instance the adapter is requested from.</param>
528520
/// <param name="surface">The surface the adapter must be compatible with.</param>
529-
/// <param name="adapter">Receives the requested adapter on success.</param>
530-
/// <param name="error">Receives the failure reason when the adapter request cannot complete.</param>
531-
/// <returns><see langword="true"/> when the adapter was returned by the native callback; otherwise <see langword="false"/>.</returns>
532-
private static bool TryRequestAdapter(
521+
/// <returns>The requested adapter.</returns>
522+
private static Adapter* RequestAdapter(
533523
WebGPU api,
534524
Instance* instance,
535-
Surface* surface,
536-
out Adapter* adapter,
537-
out string? error)
525+
Surface* surface)
538526
{
539527
RequestAdapterStatus callbackStatus = RequestAdapterStatus.Unknown;
540528
Adapter* callbackAdapter = null;
@@ -559,20 +547,15 @@ void Callback(RequestAdapterStatus status, Adapter* adapterPtr, byte* message, v
559547
api.InstanceRequestAdapter(instance, in options, callbackPtr, null);
560548
if (!callbackReady.Wait(CallbackTimeoutMilliseconds))
561549
{
562-
adapter = null;
563-
error = "Timed out while waiting for the WebGPU adapter request callback.";
564-
return false;
550+
throw new InvalidOperationException("Timed out while waiting for the WebGPU adapter request callback.");
565551
}
566552

567-
adapter = callbackAdapter;
568553
if (callbackStatus != RequestAdapterStatus.Success || callbackAdapter is null)
569554
{
570-
error = $"WebGPU adapter request failed with status '{callbackStatus}'.";
571-
return false;
555+
throw new InvalidOperationException($"The WebGPU runtime failed to acquire a surface-compatible adapter. Status: '{callbackStatus}'.");
572556
}
573557

574-
error = null;
575-
return true;
558+
return callbackAdapter;
576559
}
577560

578561
/// <summary>
@@ -583,21 +566,15 @@ void Callback(RequestAdapterStatus status, Adapter* adapterPtr, byte* message, v
583566
/// <param name="api">The shared WebGPU API loader.</param>
584567
/// <param name="adapter">The adapter the device is requested from.</param>
585568
/// <param name="requiredFeature">The feature to enable on the requested device, or <see cref="FeatureName.Undefined"/> for none.</param>
586-
/// <param name="device">Receives the requested device on success.</param>
587-
/// <param name="error">Receives the failure reason when the device request cannot complete.</param>
588-
/// <returns><see langword="true"/> when the device was returned by the native callback; otherwise <see langword="false"/>.</returns>
589-
private static bool TryRequestDevice(
569+
/// <returns>The requested device.</returns>
570+
private static Device* RequestDevice(
590571
WebGPU api,
591572
Adapter* adapter,
592-
FeatureName requiredFeature,
593-
out Device* device,
594-
out string? error)
573+
FeatureName requiredFeature)
595574
{
596575
if (requiredFeature != FeatureName.Undefined && !api.AdapterHasFeature(adapter, requiredFeature))
597576
{
598-
device = null;
599-
error = $"The selected adapter does not support required feature '{requiredFeature}'.";
600-
return false;
577+
throw new NotSupportedException($"The selected WebGPU adapter does not support required feature '{requiredFeature}'.");
601578
}
602579

603580
RequestDeviceStatus callbackStatus = RequestDeviceStatus.Unknown;
@@ -628,20 +605,15 @@ void Callback(RequestDeviceStatus status, Device* devicePtr, byte* message, void
628605
api.AdapterRequestDevice(adapter, in descriptor, callbackPtr, null);
629606
if (!callbackReady.Wait(CallbackTimeoutMilliseconds))
630607
{
631-
device = null;
632-
error = "Timed out while waiting for the WebGPU device request callback.";
633-
return false;
608+
throw new InvalidOperationException("Timed out while waiting for the WebGPU device request callback.");
634609
}
635610

636-
device = callbackDevice;
637611
if (callbackStatus != RequestDeviceStatus.Success || callbackDevice is null)
638612
{
639-
error = $"WebGPU device request failed with status '{callbackStatus}'.";
640-
return false;
613+
throw new InvalidOperationException($"The WebGPU runtime failed to acquire a device. Status: '{callbackStatus}'.");
641614
}
642615

643-
error = null;
644-
return true;
616+
return callbackDevice;
645617
}
646618

647619
/// <summary>

0 commit comments

Comments
 (0)