Skip to content

[wasm][AOT] Rgba32 decode hangs or traps unless generic pixel operation is directly rooted #130545

Description

@littletoxic

Description

A Blazor WebAssembly Release AOT build either traps with MONO_WASM: function signature mismatch or hangs while ImageSharp 4.0.0 decodes a PNG into Rgba32.

The strongest indication that this is a Mono WASM AOT generic-instantiation/codegen issue is that merely adding a reachable direct call to PixelOperations<Rgba32>.FromRgba32Bytes makes the previously failing Image.LoadAsync<Rgba32> complete. No PNG data or decoder configuration changes between the failing and working builds.

Library-side tracking with the full diagnosis is SixLabors/ImageSharp#3151.

Reproduction Steps

Clone:

git clone https://github.com/littletoxic/ImageConverterWasm.git
cd ImageConverterWasm

The app uses ImageSharp 4.0.0, which performs build-time license validation. A valid local open-source/commercial license is therefore required to build the v4 dependency. The reporter cannot publish that license. The ImageSharp maintainers can reproduce from SixLabors/ImageSharp#3151.

Publish with WASM AOT enabled:

dotnet publish .\src\ImageConverter\ImageConverter.csproj `
  -c Release `
  -p:RunAOTCompilation=true `
  -p:PublishDir="$env:TEMP\ImageConverterAot\"

dotnet serve --directory "$env:TEMP\ImageConverterAot\wwwroot"

Open the printed URL and upload:

https://github.com/littletoxic/ImageConverterWasm/blob/main/src/ImageConverter/wwwroot/favicon.png

The original app calls:

_image = await Image.LoadAsync(stream);

More focused diagnostic builds established:

// Hangs
using var rgbaImage = await Image.LoadAsync<Rgba32>(stream);

// Completes for the same RGBA PNG
using var rgbImage = await Image.LoadAsync<Rgb24>(stream);

The inverse control also hangs: an RGB PNG that normally completes hangs when explicitly decoded using Image.LoadAsync<Rgba32>.

These calls complete independently:

_ = new Image<Rgba32>(32, 32);

var source = new byte[4];
var destination = new Rgba32[1];
PixelOperations<Rgba32>.Instance.FromRgba32Bytes(
    Configuration.Default,
    source,
    destination,
    destination.Length);

Most importantly, keeping the direct FromRgba32Bytes call reachable in the application also makes the formerly hanging Image.LoadAsync<Rgba32> call complete. This changes the AOT generic instance set without changing the decoded input.

Expected behavior

Image.LoadAsync<Rgba32> should decode the 32x32 PNG and return normally in a Release WASM AOT build.

Actual behavior

With the original browser file stream, the runtime traps:

MONO_WASM: function signature mismatch
RuntimeError: function signature mismatch
    at dotnet.native.wasm:wasm-function[...]

After buffering the browser stream into a MemoryStream, the focused Image.LoadAsync<Rgba32> diagnostic hangs indefinitely before returning. Depending on which generic calls are reachable, the symptom changes between a signature-mismatch trap and a hang.

Rgb24 and several other pixel/format controls complete normally.

Regression?

ImageSharp 3.1.12 completes the same explicit Image.LoadAsync<Rgba32> test on the same .NET SDK/workload. ImageSharp 4.0.0 does not.

A relevant library code-shape change is:

  • ImageSharp 3: the generic pixel loop calls the mutable struct instance method dp.FromRgba32(sp).
  • ImageSharp 4: the loop uses the static abstract interface call TPixel.FromRgba32(...).

This establishes a library-version regression surface but does not by itself prove which runtime change is responsible.

Known Workarounds

Either:

  1. Decode into Rgb24 when dropping alpha is acceptable, or
  2. Keep a reachable direct call to PixelOperations<Rgba32>.Instance.FromRgba32Bytes(...) in the application so the required AOT instance appears to be generated.

Disabling RunAOTCompilation also avoids the AOT-specific failure.

Configuration

.NET SDK:       11.0.100-preview.5.26302.115
Runtime:        11.0.0-preview.5.26302.115
WASM workload:  11.0.100-preview.5.26302.115
OS:             Windows 10.0.26200 x64
Browser:        Microsoft Edge
ImageSharp:     4.0.0

AOT publish succeeds and reports AOT'ing 48 assemblies; the failure occurs only when the browser executes the decode path.

Other information

Related issues:

Attempts to reduce the issue to only a static-abstract-interface generic loop, including a cross-assembly version, completed normally. Therefore the report does not claim that static abstract dispatch alone is sufficient; an additional ImageSharp dispatch/instantiation condition is involved.


This issue was investigated and prepared with assistance from OpenAI Codex.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions