diff --git a/.github/workflows/uixrender-ci.yml b/.github/workflows/uixrender-ci.yml new file mode 100644 index 0000000..0dc0299 --- /dev/null +++ b/.github/workflows/uixrender-ci.yml @@ -0,0 +1,37 @@ +name: UIXrender CI + +on: + push: + pull_request: + +jobs: + build: + # UIXrender/UIXsup are implemented cross-platform (Silk.NET, no Windows-specific + # APIs), but NativeAOT can't cross-compile Linux->Windows, and the P/Invoke-based + # managed consumers we need to stay compatible with (UIX.dll et al) only run on + # Windows today -- so Windows is what Phase 0 verifies against here. Other RIDs can + # get their own job later once there's something worth publishing them for. + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: "8.0.x" + + - name: Restore + run: dotnet restore MicrosoftIris.sln + + - name: Build solution + run: dotnet build MicrosoftIris.sln -c Release --no-restore + + - name: Publish UIXrender (NativeAOT, win-x64) + run: dotnet publish UIXrender/UIXrender.csproj -c Release -f net8.0 -r win-x64 --self-contained -p:PublishAot=true + + - name: Publish UIXsup (NativeAOT, win-x64) + run: dotnet publish UIXsup/UIXsup.csproj -c Release -f net8.0 -r win-x64 --self-contained -p:PublishAot=true + + # TODO: run the Windows-only interop test harness (Tests/UIXrender.Interop.Tests) + # against the published DLLs once it exists (Phase 0 spike step) — see + # logs/UIXrender/Architecture.md and the plan for context. diff --git a/Directory.Build.props b/Directory.Build.props index 18e8638..668cdda 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -19,7 +19,9 @@ - $(TargetFrameworks);net461;net8.0-windows10.0.22000 + $(TargetFrameworks);net8.0-windows10.0.22000 + + $(TargetFrameworks);net461; diff --git a/UIX.RenderApi/UIX.RenderApi.csproj b/UIX.RenderApi/UIX.RenderApi.csproj index c2af7a6..36eadfb 100644 --- a/UIX.RenderApi/UIX.RenderApi.csproj +++ b/UIX.RenderApi/UIX.RenderApi.csproj @@ -7,5 +7,7 @@ false true + + true \ No newline at end of file diff --git a/UIX/UIX.csproj b/UIX/UIX.csproj index 43fcc1a..8367b91 100644 --- a/UIX/UIX.csproj +++ b/UIX/UIX.csproj @@ -7,6 +7,8 @@ false true + + true diff --git a/UIXrender/UIXrender.csproj b/UIXrender/UIXrender.csproj index 8db8e17..3357bf2 100644 --- a/UIXrender/UIXrender.csproj +++ b/UIXrender/UIXrender.csproj @@ -6,5 +6,12 @@ Microsoft.Iris.Render true true + + true + + + Shared \ No newline at end of file diff --git a/UIXsup/DebugCategory.cs b/UIXsup/DebugCategory.cs new file mode 100644 index 0000000..8ef3be9 --- /dev/null +++ b/UIXsup/DebugCategory.cs @@ -0,0 +1,48 @@ +namespace Microsoft.Iris.Support; + +// Mirrors Microsoft.Iris.Render.DebugCategory (UIX.RenderApi/Microsoft/Iris/Render/DebugCategory.cs) +// field-for-field: ordinal values are part of the wire contract with the already-shipped +// managed callers (DebugGetCategoryLevel/DebugSetCategoryLevel pass this by value), so +// the order here must match exactly, not just the names. +public enum DebugCategory +{ + Animation, + Critical, + Deprecated, + Dispatcher, + Dump, + Dx9Wrapper, + DynamicSurface, + Failure, + FormWindow, + GraphicsCache, + IFC, + ObjectCache, + Performance, + Profile, + Queues, + Render, + Sounds, + Surface, + SurfacePool, + SurfaceRestoration, + Timeouts, + Transport, + WindowState, + Effect, + HTTP, + ExternalCount, + Protocol, + RenderHost, + DumpException, + Focus, + Input, + TreeDisposal, + VisualSizePos, + Visual, + Painting, + RenderWindow, + ImageLoad, + ImageCache, + TotalCount, +} diff --git a/UIXsup/DebugState.cs b/UIXsup/DebugState.cs new file mode 100644 index 0000000..4199d6a --- /dev/null +++ b/UIXsup/DebugState.cs @@ -0,0 +1,22 @@ +namespace Microsoft.Iris.Support; + +internal static class DebugState +{ + private static readonly byte[] s_categoryLevels = new byte[(int)DebugCategory.TotalCount]; + + public static bool TimedWriteLines { get; set; } + public static string WriteLinePrefix { get; set; } = string.Empty; + + public static byte GetCategoryLevel(DebugCategory category) + { + int index = (int)category; + return (uint)index < (uint)s_categoryLevels.Length ? s_categoryLevels[index] : (byte)0; + } + + public static void SetCategoryLevel(DebugCategory category, byte level) + { + int index = (int)category; + if ((uint)index < (uint)s_categoryLevels.Length) + s_categoryLevels[index] = level; + } +} diff --git a/UIXsup/Interop/DebugApi.cs b/UIXsup/Interop/DebugApi.cs new file mode 100644 index 0000000..2c14ebb --- /dev/null +++ b/UIXsup/Interop/DebugApi.cs @@ -0,0 +1,38 @@ +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.Iris.Support.Interop; + +// [UnmanagedCallersOnly] exports matching the 5 DllImport("UIXsup.dll") declarations in +// the already-shipped managed callers (eDebugApi.cs / DebugHelpers.cs) field-for-field. +// See logs/UIXrender/UIXsup.md for why parameters are byte*/int here instead of +// string/bool (ANSI marshaling, blittability) and for the DebugDisplayErrorStack scope +// gap noted below. +public static unsafe class DebugApi +{ + [UnmanagedCallersOnly(EntryPoint = "DebugDisplayErrorStack")] + public static int DebugDisplayErrorStack(byte* stMessage, byte* filename, int line, byte* title, byte* stackTrace) + { + string timestamp = DebugState.TimedWriteLines ? $"[{DateTime.Now:HH:mm:ss.fff}] " : string.Empty; + Console.Error.WriteLine( + $"{timestamp}{DebugState.WriteLinePrefix}{PtrToString(title)}: {PtrToString(stMessage)} ({PtrToString(filename)}:{line})\n{PtrToString(stackTrace)}"); + + // TODO: no interactive dialog UI yet (see logs/UIXrender/UIXsup.md) -- always + // reports "don't break". + return 0; + } + + [UnmanagedCallersOnly(EntryPoint = "DebugSetTimedWriteLines")] + public static void DebugSetTimedWriteLines(int fEnabled) => DebugState.TimedWriteLines = fEnabled != 0; + + [UnmanagedCallersOnly(EntryPoint = "DebugSetWriteLinePrefix")] + public static void DebugSetWriteLinePrefix(byte* stPrefix) => DebugState.WriteLinePrefix = PtrToString(stPrefix) ?? string.Empty; + + [UnmanagedCallersOnly(EntryPoint = "DebugGetCategoryLevel")] + public static byte DebugGetCategoryLevel(DebugCategory cat) => DebugState.GetCategoryLevel(cat); + + [UnmanagedCallersOnly(EntryPoint = "DebugSetCategoryLevel")] + public static void DebugSetCategoryLevel(DebugCategory cat, byte level) => DebugState.SetCategoryLevel(cat, level); + + private static string PtrToString(byte* p) => p == null ? null : Marshal.PtrToStringAnsi((IntPtr)p); +} diff --git a/UIXsup/UIXsup.csproj b/UIXsup/UIXsup.csproj index 42f5411..ac9951d 100644 --- a/UIXsup/UIXsup.csproj +++ b/UIXsup/UIXsup.csproj @@ -6,5 +6,12 @@ Microsoft.Iris.Support true true + + true + + + Shared \ No newline at end of file