Skip to content

Commit

Permalink
feat(wasm): Add mt and mt+simd, fix harfbuzz for emsdk 3.1.12 (#2286)
Browse files Browse the repository at this point in the history
* feat(wasm): Add mt and mt+simd for emsdk 3.1.12

* chore: Adjust build flags for pthread, fix harfbuzz generation

* chore: missing variables

* chore: Adjust globbing for harbuzz wasm

* chore: Adjust flags for C compiler

* fix(SKSwapChainPanel): Invalid namespace for UWP target for Uno support

* fix(threading): Adjust support for software rendering using secure context

* ci: Bump dotnet version

* fix: ensures GL and GLctx are available, and GLctx for threading.
  • Loading branch information
jeromelaban authored Oct 20, 2022
1 parent 60b8056 commit e88acea
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 13 deletions.
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

0 comments on commit e88acea

Please sign in to comment.