Skip to content

Commit

Permalink
Fix .NET 9 ASP.NET Blazor issues (#3064) (#3081)
Browse files Browse the repository at this point in the history
* Fix .NET 9 ASP.NET Blazor issues
* Fix the build!
* Fix #3065
The wrong API was used
* fix the yaml

(cherry picked from commit af2f0c1)

Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
  • Loading branch information
github-actions[bot] and mattleibow authored Nov 27, 2024
1 parent 2273b1e commit 48916c9
Show file tree
Hide file tree
Showing 24 changed files with 321 additions and 109 deletions.
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

0 comments on commit 48916c9

Please sign in to comment.