diff --git a/binding/HarfBuzzSharp/nuget/build/wasm/HarfBuzzSharp.props b/binding/HarfBuzzSharp/nuget/build/wasm/HarfBuzzSharp.props
index 013ef410d2c..7f90974e527 100644
--- a/binding/HarfBuzzSharp/nuget/build/wasm/HarfBuzzSharp.props
+++ b/binding/HarfBuzzSharp/nuget/build/wasm/HarfBuzzSharp.props
@@ -12,7 +12,7 @@
-
+
\ No newline at end of file
diff --git a/binding/SkiaSharp/nuget/build/wasm/SkiaSharp.targets b/binding/SkiaSharp/nuget/build/wasm/SkiaSharp.targets
index b4715389656..4583939e167 100644
--- a/binding/SkiaSharp/nuget/build/wasm/SkiaSharp.targets
+++ b/binding/SkiaSharp/nuget/build/wasm/SkiaSharp.targets
@@ -1,8 +1,21 @@
-
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/native/wasm/build.cake b/native/wasm/build.cake
index 2c54effcc51..e480f9a19a6 100644
--- a/native/wasm/build.cake
+++ b/native/wasm/build.cake
@@ -24,6 +24,7 @@ Task("libSkiaSharp")
.Does(() =>
{
bool hasSimdEnabled = EMSCRIPTEN_FEATURES.Contains("simd");
+ bool hasThreadingEnabled = EMSCRIPTEN_FEATURES.Contains("mt");
GnNinja($"wasm", "SkiaSharp",
$"target_os='linux' " +
@@ -60,9 +61,11 @@ Task("libSkiaSharp")
$" '-DSKIA_C_DLL', '-DXML_POOR_ENTROPY', " +
$" {(!hasSimdEnabled ? "'-DSKNX_NO_SIMD', " : "")} '-DSK_DISABLE_AAA', '-DGR_GL_CHECK_ALLOC_WITH_GET_ERROR=0', " +
$" '-s', 'WARN_UNALIGNED=1' " + // '-s', 'USE_WEBGL2=1' (experimental)
+ $" { (hasSimdEnabled ? ", '-msimd128'" : "") } " +
+ $" { (hasThreadingEnabled ? ", '-pthread'" : "") } " +
$"] " +
// SIMD support is based on https://github.com/google/skia/blob/1f193df9b393d50da39570dab77a0bb5d28ec8ef/modules/canvaskit/compile.sh#L57
- $"extra_cflags_cc=[ '-frtti' { (hasSimdEnabled ? ", '-msimd128'" : "") } ] " +
+ $"extra_cflags_cc=[ '-frtti' { (hasSimdEnabled ? ", '-msimd128'" : "") } { (hasThreadingEnabled ? ", '-pthread'" : "") } ] " +
COMPILERS +
ADDITIONAL_GN_ARGS);
@@ -106,17 +109,24 @@ Task("libHarfBuzzSharp")
.WithCriteria(IsRunningOnLinux())
.Does(() =>
{
+ bool hasSimdEnabled = EMSCRIPTEN_FEATURES.Contains("simd");
+ bool hasThreadingEnabled = EMSCRIPTEN_FEATURES.Contains("mt");
+
GnNinja($"wasm", "HarfBuzzSharp",
$"target_os='linux' " +
$"target_cpu='wasm' " +
$"is_static_skiasharp=true " +
$"visibility_hidden=false " +
+ $"extra_cflags=[ { (hasSimdEnabled ? "'-msimd128', " : "") } { (hasThreadingEnabled ? "'-pthread'" : "") } ] " +
+ $"extra_cflags_cc=[ '-frtti' { (hasSimdEnabled ? ", '-msimd128'" : "") } { (hasThreadingEnabled ? ", '-pthread'" : "") } ] " +
COMPILERS +
ADDITIONAL_GN_ARGS);
var outDir = OUTPUT_PATH.Combine($"wasm");
if (!string.IsNullOrEmpty(EMSCRIPTEN_VERSION))
outDir = outDir.Combine("libHarfBuzzSharp.a").Combine(EMSCRIPTEN_VERSION);
+ if (EMSCRIPTEN_FEATURES.Length != 0)
+ outDir = outDir.Combine(string.Join(",", EMSCRIPTEN_FEATURES));
EnsureDirectoryExists(outDir);
var so = SKIA_PATH.CombineWithFilePath($"out/wasm/libHarfBuzzSharp.a");
CopyFileToDirectory(so, outDir);
diff --git a/scripts/azure-pipelines-variables.yml b/scripts/azure-pipelines-variables.yml
index e9a92cc5ba4..5f25da6d4cc 100644
--- a/scripts/azure-pipelines-variables.yml
+++ b/scripts/azure-pipelines-variables.yml
@@ -15,7 +15,7 @@ variables:
MONO_VERSION_LINUX: 'stable-focal/snapshots/6.12.0.182'
XCODE_VERSION: 13.2.1
VISUAL_STUDIO_VERSION: '17/pre'
- DOTNET_VERSION_PREVIEW: '6.0.401'
+ DOTNET_VERSION_PREVIEW: '6.0.402'
DOTNET_WORKLOAD_SOURCE: 'https://maui.blob.core.windows.net/metadata/rollbacks/6.0.540.json'
CONFIGURATION: 'Release'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
diff --git a/scripts/azure-templates-stages.yml b/scripts/azure-templates-stages.yml
index 277b4ad1645..42937cd2613 100644
--- a/scripts/azure-templates-stages.yml
+++ b/scripts/azure-templates-stages.yml
@@ -388,6 +388,14 @@ stages:
displayName: '3.1.12_SIMD'
version: 3.1.12
features: simd
+ - 3.1.12:
+ displayName: '3.1.12_Threading'
+ version: 3.1.12
+ features: mt
+ - 3.1.12:
+ displayName: '3.1.12_Threading_SIMD'
+ version: 3.1.12
+ features: mt,simd
- ${{ if ne(parameters.buildPipelineType, 'tests') }}:
- stage: managed
diff --git a/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/SKSwapChainPanel.Wasm.cs b/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/SKSwapChainPanel.Wasm.cs
index a454f0962ce..ea5fda39caf 100644
--- a/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/SKSwapChainPanel.Wasm.cs
+++ b/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/SKSwapChainPanel.Wasm.cs
@@ -21,7 +21,7 @@ public partial class SKSwapChainPanel : FrameworkElement
#if HAS_UNO_WINUI
const string SKSwapChainPanelTypeFullName = "SkiaSharp.Views.Windows." + nameof(SKSwapChainPanel);
#else
- const string SKSwapChainPanelTypeFullName = "SkiaSharp.Views.UWP" + nameof(SKSwapChainPanel);
+ const string SKSwapChainPanelTypeFullName = "SkiaSharp.Views.UWP." + nameof(SKSwapChainPanel);
#endif
private const int ResourceCacheBytes = 256 * 1024 * 1024; // 256 MB
diff --git a/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/SKXamlCanvas.Wasm.cs b/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/SKXamlCanvas.Wasm.cs
index 7d596cf1993..1141534c526 100644
--- a/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/SKXamlCanvas.Wasm.cs
+++ b/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/SKXamlCanvas.Wasm.cs
@@ -90,6 +90,8 @@ private SKImageInfo CreateBitmap(out SKSizeI unscaledSize, out float dpi)
private void FreeBitmap()
{
+ WebAssemblyRuntime.InvokeJS(SKXamlCanvasFullTypeName + $".clearCanvas(\"{this.GetHtmlId()}\");");
+
if (pixels != null)
{
pixelsHandle.Free();
diff --git a/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/WasmScripts/SkiaSharp.Views.Uno.Wasm.js b/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/WasmScripts/SkiaSharp.Views.Uno.Wasm.js
index de4ad9f683a..cdf2c016a97 100644
--- a/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/WasmScripts/SkiaSharp.Views.Uno.Wasm.js
+++ b/source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.Wasm/WasmScripts/SkiaSharp.Views.Uno.Wasm.js
@@ -6,6 +6,8 @@
(function (UWP) {
class SKXamlCanvas {
+ static buffers = [];
+
static invalidateCanvas(pData, canvasId, width, height) {
var htmlCanvas = document.getElementById(canvasId);
htmlCanvas.width = width;
@@ -15,12 +17,37 @@
if (!ctx)
return false;
- var buffer = new Uint8ClampedArray(Module.HEAPU8.buffer, pData, width * height * 4);
- var imageData = new ImageData(buffer, width, height);
- ctx.putImageData(imageData, 0, 0);
+ var byteLength = width * height * 4;
+
+ if (isSecureContext) {
+ // In a secure context (e.g. with threading enabled), creating a view
+ // from Module.HEAPU8.buffer is not supported, so we're making an
+ // explicit copy of the wasm memory.
+ var buffer = SKXamlCanvas.buffers[canvasId];
+
+ if (!buffer || buffer.length != byteLength) {
+ SKXamlCanvas.buffers[canvasId] = buffer = new Uint8ClampedArray(new ArrayBuffer(byteLength));
+ }
+
+ var slice = Module.HEAPU8.buffer.slice(pData, pData + byteLength);
+ buffer.set(new Uint8ClampedArray(slice), 0);
+ var imageData = new ImageData(buffer, width, height);
+ ctx.putImageData(imageData, 0, 0);
+ }
+ else {
+ var buffer = new Uint8ClampedArray(Module.HEAPU8.buffer, byteLength);
+ var imageData = new ImageData(buffer, width, height);
+ ctx.putImageData(imageData, 0, 0);
+ }
return true;
}
+
+ static clearCanvas(canvasId) {
+ if (isSecureContext) {
+ delete SKXamlCanvas.buffers[canvasId];
+ }
+ }
}
class SKSwapChainPanel {
@@ -113,14 +140,22 @@
// make current
GL.makeContextCurrent(ctx);
+ // Starting from .NET 7 the GLctx is defined in an inaccessible scope
+ // when the current GL context changes. We need to pick it up from the
+ // GL.currentContext instead.
+ let currentGLctx = GL.currentContext && GL.currentContext.GLctx;
+
+ if (!currentGLctx)
+ throw `Failed to get current WebGL context`;
+
// read values
this.canvas = canvas;
var info = {
ctx: ctx,
- fbo: GLctx.getParameter(GLctx.FRAMEBUFFER_BINDING),
- stencil: GLctx.getParameter(GLctx.STENCIL_BITS),
- sample: 0, // TODO: GLctx.getParameter(GLctx.SAMPLES)
- depth: GLctx.getParameter(GLctx.DEPTH_BITS),
+ fbo: currentGLctx.getParameter(currentGLctx.FRAMEBUFFER_BINDING),
+ stencil: currentGLctx.getParameter(currentGLctx.STENCIL_BITS),
+ sample: 0, // TODO: currentGLctx.getParameter(GLctx.SAMPLES)
+ depth: currentGLctx.getParameter(currentGLctx.DEPTH_BITS),
};
// format as array for nicer parsing