Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix .NET 9 ASP.NET Blazor issues #3064

Merged
merged 4 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions scripts/azure-pipelines-complete-internal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ parameters:
default:
pool:
name: Azure Pipelines
vmImage: macos-13
vmImage: macos-14
os: macos
- name: buildAgentLinux
displayName: 'The Linux build agent configuration:'
Expand Down Expand Up @@ -144,7 +144,7 @@ extends:
buildAgentWindows: ${{ parameters.buildAgentWindows }}
buildAgentWindowsNative: ${{ parameters.buildAgentWindows }}
buildAgentMac: ${{ parameters.buildAgentMac }}
buildAgentMacNative: ${{ parameters.buildAgentMac }}
buildAgentMacNative: ${{ parameters.buildAgentMacNative }}
buildAgentLinux: ${{ parameters.buildAgentLinux }}
buildAgentLinuxNative: ${{ parameters.buildAgentLinuxNative }}
buildAgentAndroidTests: ${{ parameters.buildAgentAndroidTests }}
2 changes: 1 addition & 1 deletion scripts/azure-pipelines-complete.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ parameters:
default:
pool:
name: Azure Pipelines
vmImage: macos-13
vmImage: macos-14
os: macos
- name: buildAgentLinux
displayName: 'The Linux build agent configuration:'
Expand Down
2 changes: 1 addition & 1 deletion scripts/azure-pipelines-variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ variables:
TIZEN_LINUX_PACKAGES: libxcb-icccm4 libxcb-render-util0 gettext libxcb-image0 libsdl1.2debian libv4l-0 libxcb-randr0 bridge-utils libxcb-shape0 libpython2.7 openvpn libkf5itemmodels5 libkf5kiowidgets5 libkchart2
MANAGED_LINUX_PACKAGES: ttf-ancient-fonts ninja-build
XCODE_VERSION: '15.4'
XCODE_VERSION_NATIVE: '14.3.1'
XCODE_VERSION_NATIVE: '15.4'
VISUAL_STUDIO_VERSION: ''
DOTNET_VERSION: '8.0.304'
DOTNET_VERSION_PREVIEW: ''
Expand Down
2 changes: 1 addition & 1 deletion scripts/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ parameters:
default:
pool:
name: Azure Pipelines
vmImage: macos-13
vmImage: macos-14
os: macos
- name: buildAgentLinux
displayName: 'The Linux build agent configuration:'
Expand Down
4 changes: 2 additions & 2 deletions scripts/azure-templates-stages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -458,11 +458,11 @@ stages:
- 3.1.56:
displayName: '3.1.56_SIMD'
version: 3.1.56
features: _wasmeh,simd,st
features: _wasmeh,st,simd
- 3.1.56:
displayName: '3.1.56_SIMD_Threading'
version: 3.1.56
features: _wasmeh,simd,mt
features: _wasmeh,mt,simd

- ${{ if ne(parameters.buildPipelineType, 'tests') }}:
- stage: native
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
#if !NET7_0_OR_GREATER
using System;
using System.ComponentModel;
using Microsoft.JSInterop;

Expand All @@ -18,3 +19,4 @@ public ActionHelper(Action action)
public void Invoke() => action?.Invoke();
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
using System;
using System;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using Microsoft.JSInterop;

#if NET7_0_OR_GREATER
using System.Runtime.InteropServices.JavaScript;
#endif

namespace SkiaSharp.Views.Blazor.Internal
{
internal class DpiWatcherInterop : JSModuleInterop
[SupportedOSPlatform("browser")]
internal partial class DpiWatcherInterop : JSModuleInterop
{
private const string ModuleName = "DpiWatcher";
private const string JsFilename = "./_content/SkiaSharp.Views.Blazor/DpiWatcher.js";
private const string StartSymbol = "DpiWatcher.start";
private const string StopSymbol = "DpiWatcher.stop";
Expand All @@ -14,9 +21,13 @@ internal class DpiWatcherInterop : JSModuleInterop
private static DpiWatcherInterop? instance;

private event Action<double>? callbacksEvent;
#if NET7_0_OR_GREATER
private readonly Action<double, double> callbackHelper;
#else
private readonly FloatFloatActionHelper callbackHelper;

private DotNetObjectReference<FloatFloatActionHelper>? callbackReference;
#endif

public static async Task<DpiWatcherInterop> ImportAsync(IJSRuntime js, Action<double>? callback = null)
{
Expand All @@ -31,9 +42,9 @@ public static DpiWatcherInterop Get(IJSRuntime js) =>
instance ??= new DpiWatcherInterop(js);

private DpiWatcherInterop(IJSRuntime js)
: base(js, JsFilename)
: base(js, ModuleName, JsFilename)
{
callbackHelper = new FloatFloatActionHelper((o, n) => callbacksEvent?.Invoke(n));
callbackHelper = new((o, n) => callbacksEvent?.Invoke((float)n));
}

protected override void OnDisposingModule() =>
Expand All @@ -60,21 +71,28 @@ public void Unsubscribe(Action<double> callback)
Stop();
}

#if NET7_0_OR_GREATER
private double Start() =>
Start(callbackHelper);

[JSImport(StartSymbol, ModuleName)]
private static partial double Start([JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>] Action<double, double> callback);

[JSImport(StopSymbol, ModuleName)]
private static partial void Stop();

[JSImport(GetDpiSymbol, ModuleName)]
public static partial double GetDpi();
#else
private double Start()
{
if (callbackReference != null)
return GetDpi();

callbackReference = DotNetObjectReference.Create(callbackHelper);
callbackReference ??= DotNetObjectReference.Create(callbackHelper);

return Invoke<double>(StartSymbol, callbackReference);
}

private void Stop()
{
if (callbackReference == null)
return;

Invoke(StopSymbol);

callbackReference?.Dispose();
Expand All @@ -83,5 +101,6 @@ private void Stop()

public double GetDpi() =>
Invoke<double>(GetDpiSymbol);
#endif
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
#if !NET7_0_OR_GREATER
using System;
using System.ComponentModel;
using Microsoft.JSInterop;

Expand All @@ -18,3 +19,4 @@ public FloatFloatActionHelper(Action<float, float> action)
public void Invoke(float width, float height) => action?.Invoke(width, height);
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
using System;
using System.Threading.Tasks;
using System.Runtime.Versioning;
using Microsoft.JSInterop;

#if NET7_0_OR_GREATER
using System.Runtime.InteropServices.JavaScript;
#else
using JSObject = Microsoft.JSInterop.IJSUnmarshalledObjectReference;
#endif

namespace SkiaSharp.Views.Blazor.Internal
{
internal class JSModuleInterop : IDisposable
[SupportedOSPlatform("browser")]
internal partial class JSModuleInterop : IDisposable
{
private readonly Task<IJSUnmarshalledObjectReference> moduleTask;
private IJSUnmarshalledObjectReference? module;
private readonly Task<JSObject> moduleTask;
private JSObject? module;

public JSModuleInterop(IJSRuntime js, string filename)
public JSModuleInterop(IJSRuntime js, string moduleName, string moduleUrl)
{
#if NET7_0_OR_GREATER
moduleTask = JSHost.ImportAsync(moduleName, "/" + moduleUrl);
#else
if (js is not IJSInProcessRuntime)
throw new NotSupportedException("SkiaSharp currently only works on Web Assembly.");

moduleTask = js.InvokeAsync<IJSUnmarshalledObjectReference>("import", filename).AsTask();
moduleTask = js.InvokeAsync<JSObject>("import", moduleUrl).AsTask();
#endif
}

public async Task ImportAsync()
Expand All @@ -28,14 +40,16 @@ public void Dispose()
Module.Dispose();
}

protected IJSUnmarshalledObjectReference Module =>
protected JSObject Module =>
module ?? throw new InvalidOperationException("Make sure to run ImportAsync() first.");

#if !NET7_0_OR_GREATER
protected void Invoke(string identifier, params object?[]? args) =>
Module.InvokeVoid(identifier, args);

protected TValue Invoke<TValue>(string identifier, params object?[]? args) =>
Module.Invoke<TValue>(identifier, args);
#endif

protected virtual void OnDisposingModule() { }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

#if NET7_0_OR_GREATER
using System.Runtime.InteropServices.JavaScript;
#endif

namespace SkiaSharp.Views.Blazor.Internal
{
internal class SKHtmlCanvasInterop : JSModuleInterop
[SupportedOSPlatform("browser")]
internal partial class SKHtmlCanvasInterop : JSModuleInterop
{
private const string ModuleName = "SKHtmlCanvas";
private const string JsFilename = "./_content/SkiaSharp.Views.Blazor/SKHtmlCanvas.js";
private const string InitGLSymbol = "SKHtmlCanvas.initGL";
private const string InitRasterSymbol = "SKHtmlCanvas.initRaster";
Expand All @@ -17,9 +24,13 @@ internal class SKHtmlCanvasInterop : JSModuleInterop

private readonly ElementReference htmlCanvas;
private readonly string htmlElementId;
#if NET7_0_OR_GREATER
private readonly Action callbackHelper;
#else
private readonly ActionHelper callbackHelper;

private DotNetObjectReference<ActionHelper>? callbackReference;
#endif

public static async Task<SKHtmlCanvasInterop> ImportAsync(IJSRuntime js, ElementReference element, Action callback)
{
Expand All @@ -29,30 +40,69 @@ public static async Task<SKHtmlCanvasInterop> ImportAsync(IJSRuntime js, Element
}

public SKHtmlCanvasInterop(IJSRuntime js, ElementReference element, Action renderFrameCallback)
: base(js, JsFilename)
: base(js, ModuleName, JsFilename)
{
htmlCanvas = element;
htmlElementId = element.Id;
htmlElementId = "_bl_" + element.Id;

callbackHelper = new ActionHelper(renderFrameCallback);
callbackHelper = new(renderFrameCallback);
}

protected override void OnDisposingModule() =>
Deinit();

#if NET7_0_OR_GREATER
public GLInfo InitGL()
{
Init();

var obj = InitGL(null, htmlElementId, callbackHelper);
var info = new GLInfo(
obj.GetPropertyAsInt32("contextId"),
(uint)obj.GetPropertyAsInt32("fboId"),
obj.GetPropertyAsInt32("stencils"),
obj.GetPropertyAsInt32("samples"),
obj.GetPropertyAsInt32("depth"));
return info;
}

[JSImport(InitGLSymbol, ModuleName)]
public static partial JSObject InitGL(JSObject? element, string elementId, [JSMarshalAs<JSType.Function>] Action callback);

public bool InitRaster()
{
Init();

return InitRaster(null, htmlElementId, callbackHelper);
}

[JSImport(InitRasterSymbol, ModuleName)]
public static partial bool InitRaster(JSObject? element, string elementId, [JSMarshalAs<JSType.Function>] Action callback);

public void Deinit() =>
Deinit(htmlElementId);

[JSImport(DeinitSymbol, ModuleName)]
public static partial void Deinit(string elementId);

public void RequestAnimationFrame(bool enableRenderLoop, int rawWidth, int rawHeight) =>
RequestAnimationFrame(htmlElementId, enableRenderLoop, rawWidth, rawHeight);

[JSImport(RequestAnimationFrameSymbol, ModuleName)]
public static partial void RequestAnimationFrame(string elementId, bool enableRenderLoop, int rawWidth, int rawHeight);

public void PutImageData(IntPtr intPtr, SKSizeI rawSize) =>
PutImageData(htmlElementId, intPtr, rawSize.Width, rawSize.Height);

[JSImport(PutImageDataSymbol, ModuleName)]
public static partial void PutImageData(string elementId, IntPtr intPtr, int rawWidth, int rawHeight);
#else
public GLInfo InitGL()
{
if (callbackReference != null)
throw new InvalidOperationException("Unable to initialize the same canvas more than once.");

try
{
InterceptGLObject();
}
catch
{
// no-op
}
Init();

callbackReference = DotNetObjectReference.Create(callbackHelper);

Expand All @@ -64,6 +114,8 @@ public bool InitRaster()
if (callbackReference != null)
throw new InvalidOperationException("Unable to initialize the same canvas more than once.");

Init();

callbackReference = DotNetObjectReference.Create(callbackHelper);

return Invoke<bool>(InitRasterSymbol, htmlCanvas, htmlElementId, callbackReference);
Expand All @@ -80,15 +132,28 @@ public void Deinit()
}

public void RequestAnimationFrame(bool enableRenderLoop, int rawWidth, int rawHeight) =>
Invoke(RequestAnimationFrameSymbol, htmlCanvas, enableRenderLoop, rawWidth, rawHeight);
Invoke(RequestAnimationFrameSymbol, htmlElementId, enableRenderLoop, rawWidth, rawHeight);

public void PutImageData(IntPtr intPtr, SKSizeI rawSize) =>
Invoke(PutImageDataSymbol, htmlCanvas, intPtr.ToInt64(), rawSize.Width, rawSize.Height);
Invoke(PutImageDataSymbol, htmlElementId, intPtr.ToInt64(), rawSize.Width, rawSize.Height);
#endif

public record GLInfo(int ContextId, uint FboId, int Stencils, int Samples, int Depth);

static void Init()
{
try
{
InterceptBrowserObjects();
}
catch
{
// no-op
}
}

// Workaround for https://github.com/dotnet/runtime/issues/76077
[DllImport("libSkiaSharp", CallingConvention = CallingConvention.Cdecl)]
static extern void InterceptGLObject();
static extern void InterceptBrowserObjects();
}
}
Loading
Loading