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

feat(wasm): Add mt and mt+simd, fix harfbuzz for emsdk 3.1.12 #2286

Merged
merged 9 commits into from
Oct 20, 2022
2 changes: 1 addition & 1 deletion binding/HarfBuzzSharp/nuget/build/wasm/HarfBuzzSharp.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</PropertyGroup>

<ItemGroup>
<HarfBuzzSharpStaticLibrary Include="$(HarfBuzzSharpStaticLibraryPath)\*\*.a" />
<HarfBuzzSharpStaticLibrary Include="$(HarfBuzzSharpStaticLibraryPath)\**\*.a" />
</ItemGroup>

</Project>
17 changes: 15 additions & 2 deletions binding/SkiaSharp/nuget/build/wasm/SkiaSharp.targets
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<ItemGroup Condition="'$(IsUnoHead)' == 'True' and '$(UnoRuntimeIdentifier)' == 'WebAssembly'">
<Choose>
<When Condition="'$(IsUnoHead)' == 'True' and '$(UnoRuntimeIdentifier)' == 'WebAssembly'">

<ItemGroup>
<Content Include="@(SkiaSharpStaticLibrary)" Visible="false" />
</ItemGroup>
</ItemGroup>

<ItemGroup>
<!-- Include the GL symbol when running under net7 (https://github.com/dotnet/runtime/issues/76077) -->
<WasmShellEmccExportedRuntimeMethod Include="GL" />

<!-- Enable GLCtx when threading is available -->
<WasmShellExtraEmccFlags Condition="'$(WasmShellEnableThreads)'=='True'" Include="-s OFFSCREEN_FRAMEBUFFER=1" />
</ItemGroup>
</When>
</Choose>

</Project>
12 changes: 11 additions & 1 deletion native/wasm/build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -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' " +
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
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 @@ -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
Expand Down
8 changes: 8 additions & 0 deletions scripts/azure-templates-stages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
(function (UWP) {

class SKXamlCanvas {
static buffers = [];

static invalidateCanvas(pData, canvasId, width, height) {
var htmlCanvas = document.getElementById(canvasId);
htmlCanvas.width = width;
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down